From c761480865c6b65e370e2bba750c86c226a7fc3f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Oct 2006 14:46:36 +0000 Subject: Add skeletons for service implementations --- audio/Makefile.am | 12 ++++++++++++ audio/main.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 audio/Makefile.am create mode 100644 audio/main.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am new file mode 100644 index 00000000..f32ed946 --- /dev/null +++ b/audio/Makefile.am @@ -0,0 +1,12 @@ + +noinst_PROGRAMS = bt.audiod + +bt_audiod_SOURCES = main.c + +bt_audiod_LDADD = @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a + +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ + +INCLUDES = -I$(top_srcdir)/common + +MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/main.c b/audio/main.c new file mode 100644 index 00000000..c04bb251 --- /dev/null +++ b/audio/main.c @@ -0,0 +1,34 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2006 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 + * 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 + +int main(int argc, char *argv[]) +{ + return 0; +} -- cgit From d0f6da96bcbb1ba20d7508294733ab46519e07b2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 13 Nov 2006 15:04:48 +0000 Subject: Add skeleton for headset daemon --- audio/Makefile.am | 6 +++++- audio/headset.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 audio/headset.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index f32ed946..054def33 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,10 +1,14 @@ -noinst_PROGRAMS = bt.audiod +noinst_PROGRAMS = bt.audiod bt.headsetd bt_audiod_SOURCES = main.c bt_audiod_LDADD = @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a +bt_headsetd_SOURCES = headset.c + +bt_headsetd_LDADD = @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a + AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ INCLUDES = -I$(top_srcdir)/common diff --git a/audio/headset.c b/audio/headset.c new file mode 100644 index 00000000..c04bb251 --- /dev/null +++ b/audio/headset.c @@ -0,0 +1,34 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2006 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 + * 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 + +int main(int argc, char *argv[]) +{ + return 0; +} -- cgit From 4cda48c7b528b3bf79dfcbaf23f982b15b6fa897 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Nov 2006 23:04:16 +0000 Subject: Very basic functionality for bt.headsetd --- audio/headset.c | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c04bb251..307b9212 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -27,8 +27,511 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dbus.h" +#include "logging.h" +#include "glib-ectomy.c" + +#define HEADSET_PATH "/org/bluez/headset" + +struct pending_connect { + bdaddr_t bda; + DBusConnection *conn; + DBusMessage *msg; + GIOChannel *io; +}; + +struct hs_connection { + char address[18]; + GIOChannel *rfcomm; + GIOChannel *sco; +}; + +static char *on_init_bda = NULL; +static int on_init_ch = 2; + +static int started = 0; + +static DBusConnection *connection = NULL; + +static GMainLoop *main_loop = NULL; + +static struct hs_connection *connected_hs = NULL; + +static int set_nonblocking(int fd, int *err) +{ + long arg; + + arg = fcntl(fd, F_GETFL); + if (arg < 0) { + if (err) + *err = errno; + error("fcntl(F_GETFL): %s (%d)", strerror(errno), errno); + return -1; + } + + /* Return if already nonblocking */ + if (arg & O_NONBLOCK) + return 0; + + arg |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, arg) < 0) { + if (err) + *err = errno; + error("fcntl(F_SETFL, O_NONBLOCK): %s (%d)", + strerror(errno), errno); + return -1; + } + + return 0; +} + +static void pending_connect_free(struct pending_connect *c, gboolean unref_io) +{ + if (unref_io && c->io) + g_io_channel_unref(c->io); + if (c->msg) + dbus_message_unref(c->msg); + if (c->conn) + dbus_connection_unref(c->conn); + free(c); +} + +static void connect_failed(DBusConnection *conn, DBusMessage *msg, int err) +{ + DBusMessage *derr; + + if (!conn) + return; + + derr = dbus_message_new_error(msg, "org.bluez.Error.ConnectFailed", + strerror(err)); + if (!derr) { + error("Unable to allocate new error return"); + return; + } + + dbus_connection_send(conn, derr, NULL); +} + +static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) +{ + int sk, ret; + unsigned char buf[1024]; + + debug("rfcomm_io_cb"); + + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } + + if (cond & (G_IO_ERR | G_IO_HUP)) + goto failed; + + sk = g_io_channel_unix_get_fd(chan); + + ret = read(sk, buf, sizeof(buf) - 1); + if (ret > 0) { + buf[ret] = '\0'; + printf("%s\n", buf); + } + + return TRUE; + +failed: + info("Disconnected from %s", hs->address); + if (hs->sco) + g_io_channel_close(hs->sco); + g_io_channel_close(chan); + free(hs); + connected_hs = NULL; + return FALSE; +} + +static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) +{ + int sk, ret, err; + socklen_t len; + struct hs_connection *hs; + + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + 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("connect(): %s (%d)", strerror(ret), ret); + goto failed; + } + + hs = malloc(sizeof(struct hs_connection)); + if (!hs) { + err = ENOMEM; + error("Allocating new hs connection struct failed!"); + goto failed; + } + + memset(hs, 0, sizeof(struct hs_connection)); + + ba2str(&c->bda, hs->address); + hs->rfcomm = chan; + + debug("rfcomm_connect_cb: connected to %s", hs->address); + + connected_hs = hs; + + g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_IN | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, hs); + + pending_connect_free(c, FALSE); + + return FALSE; + +failed: + connect_failed(c->conn, c->msg, err); + pending_connect_free(c, TRUE); + + return FALSE; +} + +static int rfcomm_connect(DBusConnection *conn, DBusMessage *msg, bdaddr_t *src, + const char *bda, uint8_t ch, int *err) +{ + struct pending_connect *c = NULL; + struct sockaddr_rc addr; + int sk; + + debug("Connecting to %s channel %d", bda, ch); + + sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) { + if (err) + *err = errno; + error("socket: %s (%d)", strerror(errno), errno); + return -1; + } + + c = malloc(sizeof(struct pending_connect)); + if (!c) { + if (err) + *err = ENOMEM; + goto failed; + } + + memset(c, 0, sizeof(struct pending_connect)); + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, src); + addr.rc_channel = 0; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (err) + *err = errno; + error("bind: %s (%d)", strerror(errno), errno); + goto failed; + } + + if (set_nonblocking(sk, err) < 0) + goto failed; + + str2ba(bda, &c->bda); + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, &c->bda); + addr.rc_channel = ch; + + if (conn && msg) { + c->conn = dbus_connection_ref(conn); + c->msg = dbus_message_ref(msg); + } + + c->io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(c->io, TRUE); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + if (err) + *err = errno; + error("connect() failed: %s (%d)", strerror(errno), errno); + goto failed; + } + + debug("Connect in progress"); + + g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, c); + } else { + debug("Connect succeeded with first try"); + rfcomm_connect_cb(c->io, G_IO_OUT, c); + } + + return 0; + +failed: + if (c) + pending_connect_free(c, TRUE); + if (sk >= 0) + close(sk); + return -1; +} + +static void sig_term(int sig) +{ + g_main_quit(main_loop); +} + +static DBusHandlerResult start_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + + info("Starting headset service"); + + reply = dbus_message_new_method_return(msg); + if (!reply) { + error("Can't create reply message"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + dbus_connection_send(conn, reply, NULL); + + dbus_message_unref(reply); + + started = 1; + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult stop_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + + info("Stopping headset service"); + + reply = dbus_message_new_method_return(msg); + if (!reply) { + error("Can't create reply message"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + dbus_connection_send(conn, reply, NULL); + + dbus_message_unref(reply); + + started = 0; + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult release_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + + reply = dbus_message_new_method_return(msg); + if (!reply) { + error("Can't create reply message"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + dbus_connection_send(conn, reply, NULL); + + dbus_message_unref(reply); + + info("Got Release method. Exiting."); + + raise(SIGTERM); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *interface; + const char *member; + + interface = dbus_message_get_interface(msg); + member = dbus_message_get_member(msg); + + if (strcmp(interface, "org.bluez.ServiceAgent") == 0) { + if (strcmp(member, "Start") == 0) + return start_message(conn, msg, data); + if (strcmp(member, "Stop") == 0) + return stop_message(conn, msg, data); + if (strcmp(member, "Release") == 0) + return release_message(conn, msg, data); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (strcmp(interface, "org.bluez.Headset") != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + /* Handle Headset interface methods here */ + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable hs_table = { + .message_function = hs_message, +}; + +static void register_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + int err; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Registering failed: %s", derr.message); + dbus_error_free(&derr); + raise(SIGTERM); + } + else + debug("Successfully registered"); + + dbus_message_unref(reply); + + if (!on_init_bda) + return; + + if (rfcomm_connect(NULL, NULL, BDADDR_ANY, on_init_bda, on_init_ch, &err) < 0) + exit(1); +} + + +int headset_dbus_init(char *bda) +{ + DBusMessage *msg; + DBusPendingCall *pending; + const char *name = "Headset service"; + const char *description = "A service for headsets"; + const char *hs_path = HEADSET_PATH; + + connection = init_dbus(NULL, NULL, NULL); + if (!connection) + return -1; + + if (!dbus_connection_register_object_path(connection, hs_path, + &hs_table, NULL)) { + error("D-Bus failed to register %s path", hs_path); + return -1; + } + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Manager", "RegisterService"); + if (!msg) { + error("Can't allocate new method call"); + return -1; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &description, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + error("Sending Register method call failed"); + dbus_message_unref(msg); + return -1; + } + + dbus_pending_call_set_notify(pending, register_reply, NULL, NULL); + dbus_message_unref(msg); + + return 0; +} + int main(int argc, char *argv[]) { + struct sigaction sa; + int opt, daemonize = 1; + + while ((opt = getopt(argc, argv, "nc:")) != EOF) { + switch (opt) { + case 'n': + daemonize = 0; + break; + + case 'c': + on_init_ch = strtol(optarg, NULL, 0); + if (on_init_ch < 0 || on_init_ch > 255) { + error("Invalid channel"); + exit(1); + } + break; + + default: + printf("Usage: %s [-n] [-c channel] [bdaddr]\n", argv[0]); + exit(1); + } + } + + if (argv[optind]) { + on_init_bda = argv[optind]; + daemonize = 0; + } + + if (daemonize && daemon(0, 0)) { + error("Can't daemonize: %s (%d)", strerror(errno), errno); + exit(1); + } + + start_logging("bt.headsetd", "Bluetooth Headset daemon"); + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + enable_debug(); + + main_loop = g_main_new(FALSE); + + if (headset_dbus_init(NULL) < 0) { + error("Unable to get on D-Bus"); + exit(1); + } + + g_main_run(main_loop); + return 0; } -- cgit From a25501d26a40907f384d223d8f87f93e5ff89ff7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Nov 2006 14:00:15 +0000 Subject: Support for fetching the RFCOMM channel through SDP --- audio/headset.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 169 insertions(+), 12 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 307b9212..0ecc2e4e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -66,7 +66,6 @@ struct hs_connection { }; static char *on_init_bda = NULL; -static int on_init_ch = 2; static int started = 0; @@ -76,6 +75,8 @@ static GMainLoop *main_loop = NULL; static struct hs_connection *connected_hs = NULL; +static int hs_connect(const char *address); + static int set_nonblocking(int fd, int *err) { long arg; @@ -411,7 +412,6 @@ static void register_reply(DBusPendingCall *call, void *data) { DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; - int err; dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { @@ -427,11 +427,10 @@ static void register_reply(DBusPendingCall *call, void *data) if (!on_init_bda) return; - if (rfcomm_connect(NULL, NULL, BDADDR_ANY, on_init_bda, on_init_ch, &err) < 0) + if (hs_connect(on_init_bda) < 0) exit(1); } - int headset_dbus_init(char *bda) { DBusMessage *msg; @@ -474,6 +473,172 @@ int headset_dbus_init(char *bda) return 0; } +static void record_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *reply; + DBusError derr; + uint8_t *array; + int array_len, record_len, ch = -1; + sdp_record_t *record = NULL; + sdp_list_t *protos; + char *address = data; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("GetRemoteServiceRecord failed: %s", derr.message); + dbus_error_free(&derr); + goto failed; + } + + dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, + DBUS_TYPE_INVALID); + + if (!array) { + error("Unable to get handle array from reply"); + goto failed; + } + + record = sdp_extract_pdu(array, &record_len); + if (!record) { + error("Unable to extract service record from reply"); + goto failed; + } + + if (record_len != array_len) + debug("warning: array len (%d) != record len (%d)", + array_len, record_len); + + if (!sdp_get_access_protos(record, &protos)) { + ch = sdp_get_proto_port(protos, RFCOMM_UUID); + sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, NULL); + sdp_list_free(protos, NULL); + } + + if (ch == -1) { + error("Unable to extract RFCOMM channel from service record"); + goto failed; + } + + if (rfcomm_connect(NULL, NULL, BDADDR_ANY, address, ch, NULL) < 0) { + error("Unable to connect to %s", address); + goto failed; + } + +failed: + if (record) + sdp_record_free(record); + dbus_message_unref(reply); + free(data); +} + +static void handles_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *msg = NULL, *reply; + DBusPendingCall *pending; + DBusError derr; + char *address = data; + dbus_uint32_t *array = NULL; + dbus_uint32_t handle; + int array_len; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("GetRemoteServiceHandles failed: %s", derr.message); + dbus_error_free(&derr); + goto failed; + } + + dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, + DBUS_TYPE_INVALID); + + if (!array) { + error("Unable to get handle array from reply"); + goto failed; + } + + if (array_len < 1) { + debug("No record handles found"); + goto failed; + } + + if (array_len > 1) + debug("Multiple records found. Using the first one."); + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "GetRemoteServiceRecord"); + if (!msg) { + error("Unable to allocate new method call"); + goto failed; + } + + handle = array[0]; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &address, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + error("Sending GetRemoteServiceRecord failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, record_reply, data, NULL); + dbus_message_unref(msg); + + dbus_message_unref(reply); + + return; + +failed: + if (msg) + dbus_message_unref(msg); + dbus_message_unref(reply); + free(data); +} + +static int hs_connect(const char *address) +{ + DBusMessage *msg; + DBusPendingCall *pending; + char *data; + const char *hs_svc = "hsp"; + + data = strdup(address); + if (!data) + return -ENOMEM; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "GetRemoteServiceHandles"); + if (!msg) { + free(data); + return -ENOMEM; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &hs_svc, + DBUS_TYPE_INVALID); + + + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + error("Sending GetRemoteServiceHandles failed"); + free(data); + dbus_message_unref(msg); + return -1; + } + + dbus_pending_call_set_notify(pending, handles_reply, data, NULL); + dbus_message_unref(msg); + + return 0; +} int main(int argc, char *argv[]) { @@ -486,14 +651,6 @@ int main(int argc, char *argv[]) daemonize = 0; break; - case 'c': - on_init_ch = strtol(optarg, NULL, 0); - if (on_init_ch < 0 || on_init_ch > 255) { - error("Invalid channel"); - exit(1); - } - break; - default: printf("Usage: %s [-n] [-c channel] [bdaddr]\n", argv[0]); exit(1); -- cgit From 5ae740ae08871959be20d3d0704b77b2bda05410 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Nov 2006 16:03:53 +0000 Subject: Minor cleanup --- audio/headset.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0ecc2e4e..ecece281 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -417,10 +417,12 @@ static void register_reply(DBusPendingCall *call, void *data) if (dbus_set_error_from_message(&derr, reply)) { error("Registering failed: %s", derr.message); dbus_error_free(&derr); + dbus_message_unref(reply); raise(SIGTERM); + return; } - else - debug("Successfully registered"); + + debug("Successfully registered headset service"); dbus_message_unref(reply); -- cgit From 26693c0cf463038c093280105ade789d71317f7a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Nov 2006 16:10:51 +0000 Subject: Remove -c from usage print since it's nolonger supported --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index ecece281..dd126814 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -654,7 +654,7 @@ int main(int argc, char *argv[]) break; default: - printf("Usage: %s [-n] [-c channel] [bdaddr]\n", argv[0]); + printf("Usage: %s [-n] [bdaddr]\n", argv[0]); exit(1); } } -- cgit From 9d11b78a36d2c8fee39f51924f9af81b663a122c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Nov 2006 16:12:54 +0000 Subject: ..and remove 'c' from getopt args too --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index dd126814..7e52ca9e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -647,7 +647,7 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt, daemonize = 1; - while ((opt = getopt(argc, argv, "nc:")) != EOF) { + while ((opt = getopt(argc, argv, "n")) != EOF) { switch (opt) { case 'n': daemonize = 0; -- cgit From f05384856c8e72cd3aabb7c8e260ee3637dd599b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Nov 2006 14:34:05 +0000 Subject: First try at Connect and Disconnect method implementation --- audio/headset.c | 231 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 167 insertions(+), 64 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 7e52ca9e..25e3c0fb 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -54,6 +54,7 @@ struct pending_connect { bdaddr_t bda; + int ch; DBusConnection *conn; DBusMessage *msg; GIOChannel *io; @@ -75,7 +76,9 @@ static GMainLoop *main_loop = NULL; static struct hs_connection *connected_hs = NULL; -static int hs_connect(const char *address); +static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, + const char *address); +static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); static int set_nonblocking(int fd, int *err) { @@ -116,21 +119,52 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io) free(c); } -static void connect_failed(DBusConnection *conn, DBusMessage *msg, int err) +static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr) { DBusMessage *derr; if (!conn) - return; + return DBUS_HANDLER_RESULT_HANDLED; - derr = dbus_message_new_error(msg, "org.bluez.Error.ConnectFailed", - strerror(err)); - if (!derr) { - error("Unable to allocate new error return"); - return; + derr = dbus_message_new_error(msg, name, descr); + if (derr) { + dbus_connection_send(conn, derr, NULL); + return DBUS_HANDLER_RESULT_HANDLED; + } else { + error("Unable to allocate new error return"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; } +} + +static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr) +{ + return error_reply(conn, msg, "org.bluez.Error.InvalidArguments", + descr ? descr : "Invalid arguments in method call"); +} + +static DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.Error.AlreadyConnected", + "Already connected to a device"); +} - dbus_connection_send(conn, derr, NULL); +static DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.Error.NotConnected", + "Not connected to any device"); +} + +static DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.Error.NotSupported", + "The service is not supported by the remote device"); +} + +static DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *msg, int err) +{ + return error_reply(conn, msg, "org.bluez.Error.ConnectFailed", strerror(err)); } static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) @@ -218,41 +252,33 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe return FALSE; failed: - connect_failed(c->conn, c->msg, err); + err_connect_failed(c->conn, c->msg, err); pending_connect_free(c, TRUE); return FALSE; } -static int rfcomm_connect(DBusConnection *conn, DBusMessage *msg, bdaddr_t *src, - const char *bda, uint8_t ch, int *err) +static int rfcomm_connect(struct pending_connect *c, int *err) { - struct pending_connect *c = NULL; struct sockaddr_rc addr; + char address[18]; int sk; - debug("Connecting to %s channel %d", bda, ch); + ba2str(&c->bda, address); + + debug("Connecting to %s channel %d", address, c->ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { if (err) *err = errno; error("socket: %s (%d)", strerror(errno), errno); - return -1; - } - - c = malloc(sizeof(struct pending_connect)); - if (!c) { - if (err) - *err = ENOMEM; goto failed; } - memset(c, 0, sizeof(struct pending_connect)); - memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, src); + bacpy(&addr.rc_bdaddr, BDADDR_ANY); addr.rc_channel = 0; if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { @@ -265,17 +291,10 @@ static int rfcomm_connect(DBusConnection *conn, DBusMessage *msg, bdaddr_t *src, if (set_nonblocking(sk, err) < 0) goto failed; - str2ba(bda, &c->bda); - memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, &c->bda); - addr.rc_channel = ch; - - if (conn && msg) { - c->conn = dbus_connection_ref(conn); - c->msg = dbus_message_ref(msg); - } + addr.rc_channel = c->ch; c->io = g_io_channel_unix_new(sk); g_io_channel_set_close_on_unref(c->io, TRUE); @@ -299,8 +318,6 @@ static int rfcomm_connect(DBusConnection *conn, DBusMessage *msg, bdaddr_t *src, return 0; failed: - if (c) - pending_connect_free(c, TRUE); if (sk >= 0) close(sk); return -1; @@ -380,8 +397,7 @@ static DBusHandlerResult release_message(DBusConnection *conn, static DBusHandlerResult hs_message(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *interface; - const char *member; + const char *interface, *member; interface = dbus_message_get_interface(msg); member = dbus_message_get_member(msg); @@ -399,6 +415,12 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(interface, "org.bluez.Headset") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + if (strcmp(member, "Connect") == 0) + return hs_connect(conn, msg, NULL); + + if (strcmp(member, "Disconnect") == 0) + return hs_disconnect(conn, msg); + /* Handle Headset interface methods here */ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -426,11 +448,8 @@ static void register_reply(DBusPendingCall *call, void *data) dbus_message_unref(reply); - if (!on_init_bda) - return; - - if (hs_connect(on_init_bda) < 0) - exit(1); + if (on_init_bda) + hs_connect(NULL, NULL, on_init_bda); } int headset_dbus_init(char *bda) @@ -480,16 +499,17 @@ static void record_reply(DBusPendingCall *call, void *data) DBusMessage *reply; DBusError derr; uint8_t *array; - int array_len, record_len, ch = -1; + int array_len, record_len, err = EIO; sdp_record_t *record = NULL; sdp_list_t *protos; - char *address = data; + struct pending_connect *c = data; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); + err_not_supported(c->conn, c->msg); dbus_error_free(&derr); goto failed; } @@ -500,12 +520,14 @@ static void record_reply(DBusPendingCall *call, void *data) if (!array) { error("Unable to get handle array from reply"); + err_not_supported(c->conn, c->msg); goto failed; } record = sdp_extract_pdu(array, &record_len); if (!record) { error("Unable to extract service record from reply"); + err_not_supported(c->conn, c->msg); goto failed; } @@ -514,26 +536,33 @@ static void record_reply(DBusPendingCall *call, void *data) array_len, record_len); if (!sdp_get_access_protos(record, &protos)) { - ch = sdp_get_proto_port(protos, RFCOMM_UUID); + c->ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, NULL); sdp_list_free(protos, NULL); } - if (ch == -1) { + if (c->ch == -1) { error("Unable to extract RFCOMM channel from service record"); + err_not_supported(c->conn, c->msg); goto failed; } - if (rfcomm_connect(NULL, NULL, BDADDR_ANY, address, ch, NULL) < 0) { - error("Unable to connect to %s", address); + if (rfcomm_connect(c, &err) < 0) { + error("Unable to connect"); + err_connect_failed(c->conn, c->msg, err); goto failed; } + sdp_record_free(record); + dbus_message_unref(reply); + + return; + failed: if (record) sdp_record_free(record); dbus_message_unref(reply); - free(data); + pending_connect_free(c, TRUE); } static void handles_reply(DBusPendingCall *call, void *data) @@ -541,7 +570,8 @@ static void handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - char *address = data; + struct pending_connect *c = data; + char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; dbus_uint32_t handle; int array_len; @@ -551,6 +581,10 @@ static void handles_reply(DBusPendingCall *call, void *data) dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); + if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed")) + err_connect_failed(c->conn, c->msg, EHOSTDOWN); + else + err_not_supported(c->conn, c->msg); dbus_error_free(&derr); goto failed; } @@ -561,11 +595,13 @@ static void handles_reply(DBusPendingCall *call, void *data) if (!array) { error("Unable to get handle array from reply"); + err_not_supported(c->conn, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); + err_not_supported(c->conn, c->msg); goto failed; } @@ -577,21 +613,25 @@ static void handles_reply(DBusPendingCall *call, void *data) "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); + err_connect_failed(c->conn, c->msg, ENOMEM); goto failed; } + ba2str(&c->bda, address); + handle = array[0]; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &address, + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); + err_connect_failed(c->conn, c->msg, EIO); goto failed; } - dbus_pending_call_set_notify(pending, record_reply, data, NULL); + dbus_pending_call_set_notify(pending, record_reply, c, NULL); dbus_message_unref(msg); dbus_message_unref(reply); @@ -602,26 +642,89 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - free(data); + pending_connect_free(c, TRUE); } -static int hs_connect(const char *address) +static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) +{ + DBusError derr; + DBusMessage *reply; + const char *address; + + dbus_error_init(&derr); + + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + err_invalid_args(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (!connected_hs || strcasecmp(address, connected_hs->address) != 0) + return err_not_connected(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (connected_hs->sco) + g_io_channel_close(connected_hs->sco); + if (connected_hs->rfcomm) + g_io_channel_close(connected_hs->rfcomm); + + free(connected_hs); + connected_hs = NULL; + + dbus_connection_send(conn, reply, NULL); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, + const char *address) { - DBusMessage *msg; DBusPendingCall *pending; - char *data; + struct pending_connect *c; const char *hs_svc = "hsp"; - data = strdup(address); - if (!data) - return -ENOMEM; + if (!address) { + DBusError derr; + + dbus_error_init(&derr); + + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + err_invalid_args(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + if (connected_hs) + return err_already_connected(conn, msg); + + c = malloc(sizeof(struct pending_connect)); + if (!c) { + error("Out of memory when allocating new struct pending_connect"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + memset(c, 0, sizeof(struct pending_connect)); msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { - free(data); - return -ENOMEM; + pending_connect_free(c, TRUE); + return DBUS_HANDLER_RESULT_NEED_MEMORY; } dbus_message_append_args(msg, DBUS_TYPE_STRING, &address, @@ -631,15 +734,15 @@ static int hs_connect(const char *address) if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); - free(data); + pending_connect_free(c, TRUE); dbus_message_unref(msg); - return -1; + return err_connect_failed(connection, msg, EIO); } - dbus_pending_call_set_notify(pending, handles_reply, data, NULL); + dbus_pending_call_set_notify(pending, handles_reply, c, NULL); dbus_message_unref(msg); - return 0; + return DBUS_HANDLER_RESULT_HANDLED;; } int main(int argc, char *argv[]) -- cgit From 6dd50a28822ea1f2cc577909758eef8927466fb7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Nov 2006 15:03:00 +0000 Subject: Implement listening rfcomm socket support --- audio/headset.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 317 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 25e3c0fb..adf4194a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,6 +51,7 @@ #include "glib-ectomy.c" #define HEADSET_PATH "/org/bluez/headset" +static const char *hs_path = HEADSET_PATH; struct pending_connect { bdaddr_t bda; @@ -66,6 +67,12 @@ struct hs_connection { GIOChannel *sco; }; +static gboolean connect_in_progress = FALSE; + +static uint8_t config_channel = 0; + +static uint32_t record_id = 0; + static char *on_init_bda = NULL; static int started = 0; @@ -76,6 +83,8 @@ static GMainLoop *main_loop = NULL; static struct hs_connection *connected_hs = NULL; +static GIOChannel *server_sk = NULL; + static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, const char *address); static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); @@ -117,6 +126,8 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io) if (c->conn) dbus_connection_unref(c->conn); free(c); + + connect_in_progress = FALSE; } static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, @@ -202,6 +213,69 @@ failed: return FALSE; } +static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +{ + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + server_sk = NULL; + return TRUE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + if (connected_hs || connect_in_progress) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + connected_hs = malloc(sizeof(struct hs_connection)); + if (!connected_hs) { + error("Allocating new hs connection struct failed!"); + close(cli_sk); + return TRUE; + } + + memset(connected_hs, 0, sizeof(struct hs_connection)); + + connected_hs->rfcomm = g_io_channel_unix_new(cli_sk); + if (!connected_hs->rfcomm) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + free(connected_hs); + connected_hs = NULL; + return TRUE; + } + + ba2str(&addr.rc_bdaddr, connected_hs->address); + + debug("rfcomm_connect_cb: connected to %s", connected_hs->address); + + g_io_add_watch(connected_hs->rfcomm, + G_IO_ERR | G_IO_HUP | G_IO_IN | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, connected_hs); + + return TRUE; +} + + static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) { int sk, ret, err; @@ -328,6 +402,217 @@ static void sig_term(int sig) g_main_quit(main_loop); } +static int server_socket(uint8_t *channel) +{ + int sock; + struct sockaddr_rc addr; + socklen_t sa_len; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return -1; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return -1; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + return sock; +} + +static int create_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *channel; + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Headset", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 uint32_t add_ag_record(uint8_t channel) +{ + DBusMessage *msg, *reply; + DBusError derr; + dbus_uint32_t rec_id; + sdp_buf_t buf; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Manager", "AddServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + if (create_ag_record(&buf, channel) < 0) { + error("Unable to allocate new service record"); + dbus_message_unref(msg); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &buf.data, buf.data_size, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); + + free(buf.data); + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { + error("Adding service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Invalid arguments to AddServiceRecord reply: %s", derr.message); + dbus_message_unref(reply); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + return rec_id; +} + +static int remove_ag_record(uint32_t rec_id) +{ + DBusMessage *msg, *reply; + DBusError derr; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Manager", "RemoveServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, + DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr)) { + error("Removing service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + return 0; +} + +static void create_server_socket(void) +{ + uint8_t chan = config_channel; + int srv_sk; + + srv_sk = server_socket(&chan); + if (srv_sk < 0) { + error("Unable to create server socket"); + return; + } + + record_id = add_ag_record(chan); + + if (!record_id) { + error("Unable to register service record"); + close(srv_sk); + return; + } + + server_sk = g_io_channel_unix_new(srv_sk); + if (!server_sk) { + error("Unable to allocate new GIOChannel"); + remove_ag_record(record_id); + return; + } + + g_io_add_watch(server_sk, G_IO_IN | G_IO_ERR | G_IO_HUP, + (GIOFunc) server_io_cb, NULL); +} + + static DBusHandlerResult start_message(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -341,6 +626,9 @@ static DBusHandlerResult start_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NEED_MEMORY; } + if (!record_id) + create_server_socket(); + dbus_connection_send(conn, reply, NULL); dbus_message_unref(reply); @@ -367,6 +655,25 @@ static DBusHandlerResult stop_message(DBusConnection *conn, dbus_message_unref(reply); + if (connected_hs) { + if (connected_hs->sco) + g_io_channel_close(connected_hs->sco); + if (connected_hs->rfcomm) + g_io_channel_close(connected_hs->rfcomm); + free(connected_hs); + connected_hs = NULL; + } + + if (!config_channel) { + remove_ag_record(record_id); + record_id = 0; + } + + if (server_sk) { + g_io_channel_close(server_sk); + server_sk = NULL; + } + started = 0; return DBUS_HANDLER_RESULT_HANDLED; @@ -448,6 +755,9 @@ static void register_reply(DBusPendingCall *call, void *data) dbus_message_unref(reply); + if (config_channel) + record_id = add_ag_record(config_channel); + if (on_init_bda) hs_connect(NULL, NULL, on_init_bda); } @@ -458,7 +768,6 @@ int headset_dbus_init(char *bda) DBusPendingCall *pending; const char *name = "Headset service"; const char *description = "A service for headsets"; - const char *hs_path = HEADSET_PATH; connection = init_dbus(NULL, NULL, NULL); if (!connection) @@ -717,6 +1026,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } + connect_in_progress = TRUE; + memset(c, 0, sizeof(struct pending_connect)); msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", @@ -750,12 +1061,16 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt, daemonize = 1; - while ((opt = getopt(argc, argv, "n")) != EOF) { + while ((opt = getopt(argc, argv, "nc:")) != EOF) { switch (opt) { case 'n': daemonize = 0; break; + case 'c': + config_channel = strtol(optarg, NULL, 0); + break; + default: printf("Usage: %s [-n] [bdaddr]\n", argv[0]); exit(1); -- cgit From cbf44a1420b70a4bd471b55943c773f42fe659b1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Nov 2006 15:27:28 +0000 Subject: Small SDP & listening rfcomm socket fixes --- audio/headset.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index adf4194a..c653786c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -547,6 +547,8 @@ static uint32_t add_ag_record(uint8_t channel) dbus_message_unref(reply); + debug("add_ag_record: got record id 0x%x"); + return rec_id; } @@ -572,7 +574,7 @@ static int remove_ag_record(uint32_t rec_id) dbus_message_unref(msg); if (dbus_error_is_set(&derr)) { - error("Removing service record failed: %s", derr.message); + error("Removing service record 0x%x failed: %s", derr.message); dbus_error_free(&derr); return 0; } @@ -593,7 +595,8 @@ static void create_server_socket(void) return; } - record_id = add_ag_record(chan); + if (!record_id) + record_id = add_ag_record(chan); if (!record_id) { error("Unable to register service record"); @@ -605,6 +608,7 @@ static void create_server_socket(void) if (!server_sk) { error("Unable to allocate new GIOChannel"); remove_ag_record(record_id); + record_id = 0; return; } @@ -626,8 +630,7 @@ static DBusHandlerResult start_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NEED_MEMORY; } - if (!record_id) - create_server_socket(); + create_server_socket(); dbus_connection_send(conn, reply, NULL); @@ -664,7 +667,7 @@ static DBusHandlerResult stop_message(DBusConnection *conn, connected_hs = NULL; } - if (!config_channel) { + if (!config_channel && record_id) { remove_ag_record(record_id); record_id = 0; } -- cgit From c9511affda16b45d41eebbce9b2b64f306cf5e86 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Nov 2006 15:34:01 +0000 Subject: Fix debug prints --- audio/headset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c653786c..8111db88 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -547,7 +547,7 @@ static uint32_t add_ag_record(uint8_t channel) dbus_message_unref(reply); - debug("add_ag_record: got record id 0x%x"); + debug("add_ag_record: got record id 0x%x", rec_id); return rec_id; } @@ -574,7 +574,7 @@ static int remove_ag_record(uint32_t rec_id) dbus_message_unref(msg); if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", derr.message); + error("Removing service record 0x%x failed: %s", rec_id, derr.message); dbus_error_free(&derr); return 0; } -- cgit From 38802a5555988b14e1927d8da6d8c198760284a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Nov 2006 16:10:33 +0000 Subject: Make the -c switch mandatory --- audio/headset.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 8111db88..277f7f42 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1075,11 +1075,16 @@ int main(int argc, char *argv[]) break; default: - printf("Usage: %s [-n] [bdaddr]\n", argv[0]); + printf("Usage: %s -c local_channel [-n] [bdaddr]\n", argv[0]); exit(1); } } + if (!config_channel) { + printf("You need to supply a local channel with the -c switch\n"); + exit(1); + } + if (argv[optind]) { on_init_bda = argv[optind]; daemonize = 0; -- cgit From b3f669cd6704e370f68fd695d2ea66212b3371a9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Nov 2006 12:01:08 +0000 Subject: Cleanup --- audio/headset.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 277f7f42..0fe95350 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -268,9 +268,8 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) debug("rfcomm_connect_cb: connected to %s", connected_hs->address); - g_io_add_watch(connected_hs->rfcomm, - G_IO_ERR | G_IO_HUP | G_IO_IN | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, connected_hs); + g_io_add_watch(connected_hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, + connected_hs); return TRUE; } @@ -280,7 +279,6 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe { int sk, ret, err; socklen_t len; - struct hs_connection *hs; if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -302,24 +300,21 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe goto failed; } - hs = malloc(sizeof(struct hs_connection)); - if (!hs) { + connected_hs = malloc(sizeof(struct hs_connection)); + if (!connected_hs) { err = ENOMEM; error("Allocating new hs connection struct failed!"); goto failed; } - memset(hs, 0, sizeof(struct hs_connection)); - - ba2str(&c->bda, hs->address); - hs->rfcomm = chan; + memset(connected_hs, 0, sizeof(struct hs_connection)); - debug("rfcomm_connect_cb: connected to %s", hs->address); + ba2str(&c->bda, connected_hs->address); + connected_hs->rfcomm = chan; - connected_hs = hs; + debug("rfcomm_connect_cb: connected to %s", connected_hs->address); - g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_IN | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs); pending_connect_free(c, FALSE); @@ -612,8 +607,7 @@ static void create_server_socket(void) return; } - g_io_add_watch(server_sk, G_IO_IN | G_IO_ERR | G_IO_HUP, - (GIOFunc) server_io_cb, NULL); + g_io_add_watch(server_sk, G_IO_IN, (GIOFunc) server_io_cb, NULL); } -- cgit From 0bf43d5dc4e1575099a2632ed46769726abeb742 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Nov 2006 12:35:42 +0000 Subject: Implement Ring method --- audio/headset.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0fe95350..f19376de 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -88,6 +88,7 @@ static GIOChannel *server_sk = NULL; static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, const char *address); static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg); static int set_nonblocking(int fd, int *err) { @@ -178,6 +179,11 @@ static DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *m return error_reply(conn, msg, "org.bluez.Error.ConnectFailed", strerror(err)); } +static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); +} + static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) { int sk, ret; @@ -449,7 +455,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(&record, root); - sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID); + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); svclass_id = sdp_list_append(0, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); @@ -725,6 +731,9 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(member, "Disconnect") == 0) return hs_disconnect(conn, msg); + if (strcmp(member, "Ring") == 0) + return hs_ring(conn, msg); + /* Handle Headset interface methods here */ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -1027,6 +1036,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, memset(c, 0, sizeof(struct pending_connect)); + str2ba(address, &c->bda); + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); @@ -1053,6 +1064,33 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED;; } +static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) +{ + DBusMessage *reply; + const char *ring_str = "RING\r"; + int sk, ret; + + if (!connected_hs) + return err_not_connected(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + sk = g_io_channel_unix_get_fd(connected_hs->rfcomm); + + ret = write(sk, ring_str, strlen(ring_str)); + if (ret < strlen(ring_str)) { + dbus_message_unref(reply); + return err_failed(conn, msg); + } + + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + int main(int argc, char *argv[]) { struct sigaction sa; -- cgit From 62281b2196470bee19d906bf5d4edba81d7e4020 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Nov 2006 13:09:53 +0000 Subject: Fix RING string --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index f19376de..d50de1a5 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1067,7 +1067,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) { DBusMessage *reply; - const char *ring_str = "RING\r"; + const char *ring_str = "\r\nRING\r\n"; int sk, ret; if (!connected_hs) -- cgit From 12bb1a4aafde063214a0c6aa2a9abffd2e079725 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Nov 2006 15:00:59 +0000 Subject: Implement ring timer and CancelRinging method --- audio/headset.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index d50de1a5..f26fcfed 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -50,6 +50,8 @@ #include "logging.h" #include "glib-ectomy.c" +#define RING_INTERVAL 3000 + #define HEADSET_PATH "/org/bluez/headset" static const char *hs_path = HEADSET_PATH; @@ -65,6 +67,7 @@ struct hs_connection { char address[18]; GIOChannel *rfcomm; GIOChannel *sco; + guint ring_timer; }; static gboolean connect_in_progress = FALSE; @@ -89,6 +92,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, const char *address); static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg); static int set_nonblocking(int fd, int *err) { @@ -207,6 +211,11 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_conn printf("%s\n", buf); } + if (connected_hs->ring_timer) { + g_timeout_remove(connected_hs->ring_timer); + connected_hs->ring_timer = 0; + } + return TRUE; failed: @@ -734,6 +743,9 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(member, "Ring") == 0) return hs_ring(conn, msg); + if (strcmp(member, "CancelRinging") == 0) + return hs_cancel_ringing(conn, msg); + /* Handle Headset interface methods here */ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -1064,11 +1076,41 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED;; } +static int send_ring(GIOChannel *io) +{ + const char *ring_str = "\r\nRING\r\n"; + int sk, written, len; + + sk = g_io_channel_unix_get_fd(connected_hs->rfcomm); + + len = strlen(ring_str); + written = 0; + + while (written < len) { + int ret; + + ret = write(sk, ring_str + written, len - written); + + if (ret < 0) + return ret; + + written += ret; + } + + return 0; +} + +static gboolean ring_timer(gpointer user_data) +{ + if (send_ring(connected_hs->rfcomm) < 0) + error("Sending RING failed"); + + return TRUE; +} + static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) { DBusMessage *reply; - const char *ring_str = "\r\nRING\r\n"; - int sk, ret; if (!connected_hs) return err_not_connected(conn, msg); @@ -1077,14 +1119,45 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - sk = g_io_channel_unix_get_fd(connected_hs->rfcomm); + if (connected_hs->ring_timer) { + debug("Got Ring method call while ringing already in progress"); + goto done; + } - ret = write(sk, ring_str, strlen(ring_str)); - if (ret < strlen(ring_str)) { + if (send_ring(connected_hs->rfcomm) < 0) { dbus_message_unref(reply); return err_failed(conn, msg); } + connected_hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL); + +done: + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg) +{ + DBusMessage *reply; + + if (!connected_hs) + return err_not_connected(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (!connected_hs->ring_timer) { + debug("Got CancelRinging method call but ringing is not in progress"); + goto done; + } + + g_timeout_remove(connected_hs->ring_timer); + connected_hs->ring_timer = 0; + +done: dbus_connection_send(conn, reply, NULL); dbus_message_unref(reply); -- cgit From e8dc0cc060fa03ac83cb8fc04679c79607adc023 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Nov 2006 10:12:34 +0000 Subject: Implement AT command parsing --- audio/headset.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 119 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index f26fcfed..350f5973 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -50,6 +50,8 @@ #include "logging.h" #include "glib-ectomy.c" +#define BUF_SIZE 1024 + #define RING_INTERVAL 3000 #define HEADSET_PATH "/org/bluez/headset" @@ -65,9 +67,15 @@ struct pending_connect { struct hs_connection { char address[18]; + GIOChannel *rfcomm; GIOChannel *sco; + guint ring_timer; + + char buf[BUF_SIZE]; + int data_start; + int data_length; }; static gboolean connect_in_progress = FALSE; @@ -188,12 +196,49 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); } +static void send_gain_setting(const char *buf) +{ +} + +static void send_button_press(void) +{ + DBusMessage *signal; + + signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", + "AnswerRequested"); + if (!signal) { + error("Unable to allocate new AnswerRequested signal"); + return; + } + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); +} + +static void parse_headset_event(const char *buf, char *rsp, int rsp_len) +{ + printf("Received: %s\n", buf); + + /* Return an error if this is not a proper AT command */ + if (strncmp(buf, "AT", 2)) { + snprintf(rsp, rsp_len, "\r\nERROR\r\n"); + return; + } + + buf += 2; + + if (!strncmp(buf, "+CKPD", 5)) + send_button_press(); + else if (!strncmp(buf, "+VG", 3)) + send_gain_setting(buf); + + snprintf(rsp, rsp_len, "\r\nOK\r\n"); +} + static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) { int sk, ret; - unsigned char buf[1024]; - - debug("rfcomm_io_cb"); + unsigned char buf[BUF_SIZE]; if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -207,8 +252,59 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_conn ret = read(sk, buf, sizeof(buf) - 1); if (ret > 0) { - buf[ret] = '\0'; - printf("%s\n", buf); + int free_space = sizeof(connected_hs->buf) - + connected_hs->data_start - + connected_hs->data_length - 1; + char *cr; + + if (free_space < ret) { + error("Too much data to fit incomming buffer"); + goto failed; + } + + memcpy(&connected_hs->buf[connected_hs->data_start], buf, ret); + connected_hs->data_length += ret; + + /* Make sure the data is null terminated so we can use string + * functions */ + connected_hs->buf[connected_hs->data_length] = '\0'; + + cr = strchr(&connected_hs->buf[connected_hs->data_start], '\r'); + if (cr) { + char rsp[BUF_SIZE]; + int len, written = 0; + off_t cmd_len = 1 + (off_t) cr - + (off_t) &connected_hs->buf[connected_hs->data_start]; + + *cr = '\0'; + + memset(rsp, 0, sizeof(rsp)); + + parse_headset_event(&connected_hs->buf[connected_hs->data_start], + rsp, sizeof(rsp)); + + len = strlen(rsp); + + while (written < len) { + int ret; + + ret = write(sk, &rsp[written], len - written); + if (ret < 0) { + error("write: %s (%d)", errno, strerror(errno)); + break; + } + + written += ret; + } + + connected_hs->data_start += cmd_len; + connected_hs->data_length -= cmd_len; + + if (!connected_hs->data_length) + connected_hs->data_start = 0; + + } + } if (connected_hs->ring_timer) { @@ -281,7 +377,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) ba2str(&addr.rc_bdaddr, connected_hs->address); - debug("rfcomm_connect_cb: connected to %s", connected_hs->address); + debug("Accepted connection from %s", connected_hs->address); g_io_add_watch(connected_hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs); @@ -327,10 +423,20 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe ba2str(&c->bda, connected_hs->address); connected_hs->rfcomm = chan; - debug("rfcomm_connect_cb: connected to %s", connected_hs->address); + debug("Connected to %s", connected_hs->address); g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs); + if (c->msg) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(c->msg); + if (reply) { + dbus_connection_send(c->conn, reply, NULL); + dbus_message_unref(reply); + } + } + pending_connect_free(c, FALSE); return FALSE; @@ -1002,6 +1108,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) if (connected_hs->rfcomm) g_io_channel_close(connected_hs->rfcomm); + info("Disconnected from %s", connected_hs->address); + free(connected_hs); connected_hs = NULL; @@ -1050,6 +1158,9 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, str2ba(address, &c->bda); + c->conn = dbus_connection_ref(conn); + c->msg = dbus_message_ref(msg); + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); @@ -1063,7 +1174,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); pending_connect_free(c, TRUE); dbus_message_unref(msg); -- cgit From 6834de18b98fea0e7d370a296318115a659047af Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Nov 2006 15:41:08 +0000 Subject: More implementation. E.g. signals and Play and Stop methods --- audio/headset.c | 343 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 266 insertions(+), 77 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 350f5973..4491fc2e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -65,7 +65,7 @@ struct pending_connect { GIOChannel *io; }; -struct hs_connection { +struct headset { char address[18]; GIOChannel *rfcomm; @@ -78,7 +78,7 @@ struct hs_connection { int data_length; }; -static gboolean connect_in_progress = FALSE; +static struct pending_connect *connect_in_progress = NULL; static uint8_t config_channel = 0; @@ -92,7 +92,7 @@ static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; -static struct hs_connection *connected_hs = NULL; +static struct headset *hs = NULL; static GIOChannel *server_sk = NULL; @@ -101,6 +101,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg); static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg); +static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg); static int set_nonblocking(int fd, int *err) { @@ -140,7 +142,7 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io) dbus_connection_unref(c->conn); free(c); - connect_in_progress = FALSE; + connect_in_progress = NULL; } static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, @@ -198,14 +200,14 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) static void send_gain_setting(const char *buf) { + /* Not yet implemented */ } -static void send_button_press(void) +static void send_simple_signal(const char *name) { DBusMessage *signal; - signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", - "AnswerRequested"); + signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name); if (!signal) { error("Unable to allocate new AnswerRequested signal"); return; @@ -228,14 +230,14 @@ static void parse_headset_event(const char *buf, char *rsp, int rsp_len) buf += 2; if (!strncmp(buf, "+CKPD", 5)) - send_button_press(); + send_simple_signal("AnswerRequested"); else if (!strncmp(buf, "+VG", 3)) send_gain_setting(buf); snprintf(rsp, rsp_len, "\r\nOK\r\n"); } -static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_connection *hs) +static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { int sk, ret; unsigned char buf[BUF_SIZE]; @@ -252,75 +254,81 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct hs_conn ret = read(sk, buf, sizeof(buf) - 1); if (ret > 0) { - int free_space = sizeof(connected_hs->buf) - - connected_hs->data_start - - connected_hs->data_length - 1; + int free_space; char *cr; + free_space = sizeof(hs->buf) - hs->data_start + - hs->data_length - 1; + if (free_space < ret) { + /* Very likely that the HS is sending us garbage so + * just ignore the data and disconnect */ error("Too much data to fit incomming buffer"); goto failed; } - memcpy(&connected_hs->buf[connected_hs->data_start], buf, ret); - connected_hs->data_length += ret; + memcpy(&hs->buf[hs->data_start], buf, ret); + hs->data_length += ret; /* Make sure the data is null terminated so we can use string * functions */ - connected_hs->buf[connected_hs->data_length] = '\0'; + hs->buf[hs->data_length] = '\0'; - cr = strchr(&connected_hs->buf[connected_hs->data_start], '\r'); + cr = strchr(&hs->buf[hs->data_start], '\r'); if (cr) { char rsp[BUF_SIZE]; - int len, written = 0; - off_t cmd_len = 1 + (off_t) cr - - (off_t) &connected_hs->buf[connected_hs->data_start]; - + int len, written; + off_t cmd_len; + + cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; *cr = '\0'; memset(rsp, 0, sizeof(rsp)); - parse_headset_event(&connected_hs->buf[connected_hs->data_start], + parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp)); len = strlen(rsp); + written = 0; while (written < len) { int ret; ret = write(sk, &rsp[written], len - written); if (ret < 0) { - error("write: %s (%d)", errno, strerror(errno)); + error("write: %s (%d)", + strerror(errno), errno); break; } written += ret; } - connected_hs->data_start += cmd_len; - connected_hs->data_length -= cmd_len; + hs->data_start += cmd_len; + hs->data_length -= cmd_len; - if (!connected_hs->data_length) - connected_hs->data_start = 0; + if (!hs->data_length) + hs->data_start = 0; } } - if (connected_hs->ring_timer) { - g_timeout_remove(connected_hs->ring_timer); - connected_hs->ring_timer = 0; + if (hs->ring_timer) { + g_timeout_remove(hs->ring_timer); + hs->ring_timer = 0; } return TRUE; failed: info("Disconnected from %s", hs->address); + send_simple_signal("Disconnected"); if (hs->sco) g_io_channel_close(hs->sco); g_io_channel_close(chan); free(hs); - connected_hs = NULL; + hs = NULL; return FALSE; } @@ -351,40 +359,110 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (connected_hs || connect_in_progress) { + if (hs || connect_in_progress) { debug("Refusing new connection since one already exists"); close(cli_sk); return TRUE; } - connected_hs = malloc(sizeof(struct hs_connection)); - if (!connected_hs) { + hs = malloc(sizeof(struct headset)); + if (!hs) { error("Allocating new hs connection struct failed!"); close(cli_sk); return TRUE; } - memset(connected_hs, 0, sizeof(struct hs_connection)); + memset(hs, 0, sizeof(struct headset)); - connected_hs->rfcomm = g_io_channel_unix_new(cli_sk); - if (!connected_hs->rfcomm) { + hs->rfcomm = g_io_channel_unix_new(cli_sk); + if (!hs->rfcomm) { error("Allocating new GIOChannel failed!"); close(cli_sk); - free(connected_hs); - connected_hs = NULL; + free(hs); + hs = NULL; return TRUE; } - ba2str(&addr.rc_bdaddr, connected_hs->address); + ba2str(&addr.rc_bdaddr, hs->address); + + debug("Accepted connection from %s", hs->address); + + send_simple_signal("Connected"); + + g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, + hs); + + return TRUE; +} + +static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +{ + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } - debug("Accepted connection from %s", connected_hs->address); + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Audio connection got disconnected"); + g_io_channel_close(chan); + hs->sco = NULL; + send_simple_signal("Stopped"); + return FALSE; + } - g_io_add_watch(connected_hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, - connected_hs); + debug("sco_io_cb: Unhandled IO condition"); return TRUE; } +static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, + struct pending_connect *c) +{ + int ret, sk, err; + DBusMessage *reply; + socklen_t len; + + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + 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("connect(): %s (%d)", strerror(ret), ret); + goto failed; + } + + hs->sco = chan; + g_io_add_watch(chan, 0, sco_io_cb, NULL); + + reply = dbus_message_new_method_return(c->msg); + if (reply) { + dbus_connection_send(c->conn, reply, NULL); + dbus_message_unref(reply); + } + + pending_connect_free(c, FALSE); + + send_simple_signal("Playing"); + + return FALSE; + +failed: + err_connect_failed(c->conn, c->msg, err); + pending_connect_free(c, TRUE); + + return FALSE; +} static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) { @@ -411,21 +489,23 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe goto failed; } - connected_hs = malloc(sizeof(struct hs_connection)); - if (!connected_hs) { + hs = malloc(sizeof(struct headset)); + if (!hs) { err = ENOMEM; error("Allocating new hs connection struct failed!"); goto failed; } - memset(connected_hs, 0, sizeof(struct hs_connection)); + memset(hs, 0, sizeof(struct headset)); - ba2str(&c->bda, connected_hs->address); - connected_hs->rfcomm = chan; + ba2str(&c->bda, hs->address); + hs->rfcomm = chan; - debug("Connected to %s", connected_hs->address); + send_simple_signal("Connected"); - g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, connected_hs); + debug("Connected to %s", hs->address); + + g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); if (c->msg) { DBusMessage *reply; @@ -773,13 +853,13 @@ static DBusHandlerResult stop_message(DBusConnection *conn, dbus_message_unref(reply); - if (connected_hs) { - if (connected_hs->sco) - g_io_channel_close(connected_hs->sco); - if (connected_hs->rfcomm) - g_io_channel_close(connected_hs->rfcomm); - free(connected_hs); - connected_hs = NULL; + if (hs) { + if (hs->sco) + g_io_channel_close(hs->sco); + if (hs->rfcomm) + g_io_channel_close(hs->rfcomm); + free(hs); + hs = NULL; } if (!config_channel && record_id) { @@ -852,7 +932,11 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(member, "CancelRinging") == 0) return hs_cancel_ringing(conn, msg); - /* Handle Headset interface methods here */ + if (strcmp(member, "Play") == 0) + return hs_play(conn, msg); + + if (strcmp(member, "Stop") == 0) + return hs_stop(conn, msg); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1096,22 +1180,24 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } - if (!connected_hs || strcasecmp(address, connected_hs->address) != 0) + if (!hs || strcasecmp(address, hs->address) != 0) return err_not_connected(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (connected_hs->sco) - g_io_channel_close(connected_hs->sco); - if (connected_hs->rfcomm) - g_io_channel_close(connected_hs->rfcomm); + if (hs->sco) + g_io_channel_close(hs->sco); + if (hs->rfcomm) + g_io_channel_close(hs->rfcomm); - info("Disconnected from %s", connected_hs->address); + info("Disconnected from %s", hs->address); + + send_simple_signal("Disconnected"); - free(connected_hs); - connected_hs = NULL; + free(hs); + hs = NULL; dbus_connection_send(conn, reply, NULL); @@ -1143,7 +1229,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, } } - if (connected_hs) + if (hs) return err_already_connected(conn, msg); c = malloc(sizeof(struct pending_connect)); @@ -1152,7 +1238,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } - connect_in_progress = TRUE; + connect_in_progress = c; memset(c, 0, sizeof(struct pending_connect)); @@ -1192,7 +1278,7 @@ static int send_ring(GIOChannel *io) const char *ring_str = "\r\nRING\r\n"; int sk, written, len; - sk = g_io_channel_unix_get_fd(connected_hs->rfcomm); + sk = g_io_channel_unix_get_fd(hs->rfcomm); len = strlen(ring_str); written = 0; @@ -1213,7 +1299,7 @@ static int send_ring(GIOChannel *io) static gboolean ring_timer(gpointer user_data) { - if (send_ring(connected_hs->rfcomm) < 0) + if (send_ring(hs->rfcomm) < 0) error("Sending RING failed"); return TRUE; @@ -1223,24 +1309,24 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) { DBusMessage *reply; - if (!connected_hs) + if (!hs) return err_not_connected(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (connected_hs->ring_timer) { + if (hs->ring_timer) { debug("Got Ring method call while ringing already in progress"); goto done; } - if (send_ring(connected_hs->rfcomm) < 0) { + if (send_ring(hs->rfcomm) < 0) { dbus_message_unref(reply); return err_failed(conn, msg); } - connected_hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL); + hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL); done: dbus_connection_send(conn, reply, NULL); @@ -1253,20 +1339,20 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms { DBusMessage *reply; - if (!connected_hs) + if (!hs) return err_not_connected(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (!connected_hs->ring_timer) { + if (!hs->ring_timer) { debug("Got CancelRinging method call but ringing is not in progress"); goto done; } - g_timeout_remove(connected_hs->ring_timer); - connected_hs->ring_timer = 0; + g_timeout_remove(hs->ring_timer); + hs->ring_timer = 0; done: dbus_connection_send(conn, reply, NULL); @@ -1275,6 +1361,109 @@ done: return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg) +{ + struct sockaddr_sco addr; + struct pending_connect *c; + int sk, err; + + if (!hs) + return err_not_connected(conn, msg); + + if (hs->sco) + return err_already_connected(conn, msg); + + c = malloc(sizeof(struct pending_connect)); + if (!c) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + memset(c, 0, sizeof(struct pending_connect)); + + c->conn = dbus_connection_ref(conn); + c->msg = dbus_message_ref(msg); + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + err_connect_failed(conn, msg, err); + goto failed; + } + + c->io = g_io_channel_unix_new(sk); + if (!c->io) { + close(sk); + pending_connect_free(c, TRUE); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + g_io_channel_set_close_on_unref(c->io, TRUE); + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, BDADDR_ANY); + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + err_connect_failed(conn, msg, err); + goto failed; + } + + if (set_nonblocking(sk, &err) < 0) { + err_connect_failed(conn, msg, err); + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + str2ba(hs->address, &addr.sco_bdaddr); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect: %s (%d)", strerror(errno), errno); + goto failed; + } + + debug("Connect in progress"); + + g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c); + } else { + debug("Connect succeeded with first try"); + sco_connect_cb(c->io, G_IO_OUT, c); + } + + return 0; + +failed: + if (c) + pending_connect_free(c, TRUE); + close(sk); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) +{ + DBusMessage *reply; + + if (!hs || !hs->sco) + return err_not_connected(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + g_io_channel_close(hs->sco); + hs->sco = NULL; + + send_simple_signal("Stopped"); + + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + int main(int argc, char *argv[]) { struct sigaction sa; -- cgit From 954a25724ff381df97c0cc15bda110b9da4273ea Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Nov 2006 12:48:08 +0000 Subject: Implement SpeakerGainChanged and MicrophoneGainChanged signals --- audio/headset.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 4491fc2e..742bf558 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -200,7 +200,40 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) static void send_gain_setting(const char *buf) { - /* Not yet implemented */ + const char *name; + DBusMessage *signal; + dbus_uint16_t gain; + + if (strlen(buf) < 6) { + error("Too short string for Gain setting"); + return; + } + + switch (buf[3]) { + case 'S': + name = "SpeakerGainChanged"; + break; + case 'M': + name = "MicrophoneGainChanged"; + break; + default: + error("Unknown gain setting"); + return; + } + + signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name); + if (!signal) { + error("Unable to allocate new GainChanged signal"); + return; + } + + gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); + + dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); } static void send_simple_signal(const char *name) -- cgit From 842d31218419f535f11a1f4e149662efd5adb82e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Nov 2006 13:26:25 +0000 Subject: Add simple introspection support --- audio/headset.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 742bf558..8910c864 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -950,6 +950,10 @@ static DBusHandlerResult hs_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } + if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && + !strcmp("Introspect", member)) + return simple_introspect(conn, msg, data); + if (strcmp(interface, "org.bluez.Headset") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -- cgit From adcfee2f842e6d25c5b7a9c34a4ee0bbcdfe300b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Nov 2006 14:23:59 +0000 Subject: Support for providing input and output files for audio --- audio/headset.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 8910c864..8098e8ed 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -71,6 +71,9 @@ struct headset { GIOChannel *rfcomm; GIOChannel *sco; + GIOChannel *audio_input; + int out; + guint ring_timer; char buf[BUF_SIZE]; @@ -96,6 +99,9 @@ static struct headset *hs = NULL; static GIOChannel *server_sk = NULL; +static char *audio_input = NULL; +static char *audio_output = NULL; + static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, const char *address); static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); @@ -359,6 +365,8 @@ failed: send_simple_signal("Disconnected"); if (hs->sco) g_io_channel_close(hs->sco); + if (hs->out >= 0) + close(hs->out); g_io_channel_close(chan); free(hs); hs = NULL; @@ -407,6 +415,8 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) memset(hs, 0, sizeof(struct headset)); + hs->out = -1; + hs->rfcomm = g_io_channel_unix_new(cli_sk); if (!hs->rfcomm) { error("Allocating new GIOChannel failed!"); @@ -428,10 +438,87 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } +static gboolean audio_input_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +{ + int in, out, data_size, written; + char buf[1024]; + + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + hs->audio_input = NULL; + return FALSE; + } + + if (cond & (G_IO_HUP | G_IO_ERR)) { + g_io_channel_close(hs->audio_input); + hs->audio_input = NULL; + if (hs->out >= 0) { + close(hs->out); + hs->out = -1; + } + return FALSE; + } + + in = g_io_channel_unix_get_fd(chan); + out = g_io_channel_unix_get_fd(hs->sco); + + data_size = read(in, buf, sizeof(buf)); + if (data_size < 0) { + error("read: %s (%d)", strerror(errno), errno); + g_io_channel_close(chan); + hs->audio_input = NULL; + return TRUE; + } + + /* EOF */ + if (data_size == 0) { + debug("Reached end of file"); + g_io_channel_close(chan); + hs->audio_input = NULL; + return TRUE; + } + + written = 0; + + while (written < data_size) { + int ret; + + ret = write(out, &buf[written], data_size - written); + + if (ret < 0) { + error("write(%d, %p, %d): %s (%d)", out, &buf[data_size], + data_size - written, strerror(errno), errno); + g_io_channel_close(chan); + hs->audio_input = NULL; + return TRUE; + } + + debug("wrote %d bytes to %s", ret, audio_output); + + written += ret; + } + + return TRUE; +} + static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { + int in, ret; + char buf[1024]; + if (cond & G_IO_NVAL) { g_io_channel_unref(chan); + if (hs) { + if (hs->audio_input) { + g_io_channel_close(hs->audio_input); + hs->audio_input = NULL; + } + if (hs->out >= 0) { + close(hs->out); + hs->out = -1; + } + } + return FALSE; } @@ -439,11 +526,32 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_dat error("Audio connection got disconnected"); g_io_channel_close(chan); hs->sco = NULL; + if (hs->audio_input) { + g_io_channel_close(hs->audio_input); + hs->audio_input = NULL; + } send_simple_signal("Stopped"); return FALSE; } - debug("sco_io_cb: Unhandled IO condition"); + if (!audio_output) { + debug("sco_io_cb: Unhandled IO condition"); + return TRUE; + } + + in = g_io_channel_unix_get_fd(chan); + if (hs->out < 0) + hs->out = open(audio_output, O_WRONLY | O_SYNC | O_CREAT); + + if (hs->out < 0) { + error("open(%s): %s (%d)", audio_output, strerror(errno), errno); + g_io_channel_close(chan); + return TRUE; + } + + ret = read(in, buf, sizeof(buf)); + if (ret > 0) + ret = write(hs->out, buf, ret); return TRUE; } @@ -451,7 +559,7 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_dat static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) { - int ret, sk, err; + int ret, sk, err, flags; DBusMessage *reply; socklen_t len; @@ -475,8 +583,15 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } + debug("SCO socket %d opened", sk); + + if (audio_output) + flags = G_IO_IN; + else + flags = 0; + hs->sco = chan; - g_io_add_watch(chan, 0, sco_io_cb, NULL); + g_io_add_watch(chan, flags, sco_io_cb, NULL); reply = dbus_message_new_method_return(c->msg); if (reply) { @@ -484,6 +599,20 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, dbus_message_unref(reply); } + if (audio_input) { + int in; + + in = open(audio_input, O_RDONLY | O_NOCTTY); + + if (in < 0) + error("open(%s): %s %d", audio_input, strerror(errno), errno); + else { + hs->audio_input = g_io_channel_unix_new(in); + g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_cb, NULL); + } + + } + pending_connect_free(c, FALSE); send_simple_signal("Playing"); @@ -531,6 +660,8 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe memset(hs, 0, sizeof(struct headset)); + hs->out = -1; + ba2str(&c->bda, hs->address); hs->rfcomm = chan; @@ -891,6 +1022,10 @@ static DBusHandlerResult stop_message(DBusConnection *conn, g_io_channel_close(hs->sco); if (hs->rfcomm) g_io_channel_close(hs->rfcomm); + if (hs->out >= 0) { + close(hs->out); + hs->out = -1; + } free(hs); hs = NULL; } @@ -1226,6 +1361,10 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) if (hs->sco) g_io_channel_close(hs->sco); + if (hs->out >= 0) { + close(hs->out); + hs->out = -1; + } if (hs->rfcomm) g_io_channel_close(hs->rfcomm); @@ -1493,6 +1632,11 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) g_io_channel_close(hs->sco); hs->sco = NULL; + if (hs->out >= 0) { + close(hs->out); + hs->out = -1; + } + send_simple_signal("Stopped"); dbus_connection_send(conn, reply, NULL); @@ -1506,7 +1650,7 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt, daemonize = 1; - while ((opt = getopt(argc, argv, "nc:")) != EOF) { + while ((opt = getopt(argc, argv, "nc:o:i:")) != EOF) { switch (opt) { case 'n': daemonize = 0; @@ -1516,8 +1660,16 @@ int main(int argc, char *argv[]) config_channel = strtol(optarg, NULL, 0); break; + case 'i': + audio_input = optarg; + break; + + case 'o': + audio_output = optarg; + break; + default: - printf("Usage: %s -c local_channel [-n] [bdaddr]\n", argv[0]); + printf("Usage: %s -c local_channel [-n] [-o output] [-i input] [bdaddr]\n", argv[0]); exit(1); } } -- cgit From 539522aa346cd7d49f5665ee83a5659e3464d83c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 6 Dec 2006 13:58:19 +0000 Subject: Add initial version of the audio API document --- audio/Makefile.am | 2 ++ audio/audio-api.txt | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 audio/audio-api.txt (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 054def33..85025d5b 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -13,4 +13,6 @@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ INCLUDES = -I$(top_srcdir)/common +EXTRA_DIST = audio-api.txt + MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/audio-api.txt b/audio/audio-api.txt new file mode 100644 index 00000000..9427ca71 --- /dev/null +++ b/audio/audio-api.txt @@ -0,0 +1,41 @@ +Audio API description for BlueZ +******************************* + +Copyright (C) 2004-2006 Marcel Holtmann +Copyright (C) 2005-2006 Johan Hedberg +Copyright (C) 2005-2006 Brad Midgley + + +Audio hierarchy +=============== + +Service org.bluez.audio +Interface org.bluez.Audio +Object path /org/bluez/audio + +Methods array{string} ListHeadsets() + + Returns list of Bluetooth addresses for devices that + are configured as headsets. + + string GetDefaultHeadset() + + Returns the Bluetooth address of the default headset + device. + + void SetDefaultHeadset(string address) + + void CreateHeadset(string address) + + void RemoveHeadset(string address) + + string ConnectHeadset(string address) + + Connects to the headset and setups a new stream. The + new stream identifier is returned. + +Signals void HeadsetCreated(string address) + + void HeadsetRemoved(string address) + + void DefaultHeadsetChanged(string address) -- cgit From 1eac882c2722eee74b595d8e3af69f3ab10228ae Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 19 Dec 2006 09:31:43 +0000 Subject: Update method names to resemble more closely the current API draft --- audio/headset.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 8098e8ed..2bda011d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -348,9 +348,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ if (!hs->data_length) hs->data_start = 0; - } - } if (hs->ring_timer) { @@ -1092,16 +1090,16 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(interface, "org.bluez.Headset") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (strcmp(member, "Connect") == 0) + if (strcmp(member, "ConnectHeadset") == 0) return hs_connect(conn, msg, NULL); if (strcmp(member, "Disconnect") == 0) return hs_disconnect(conn, msg); - if (strcmp(member, "Ring") == 0) + if (strcmp(member, "IndicateCall") == 0) return hs_ring(conn, msg); - if (strcmp(member, "CancelRinging") == 0) + if (strcmp(member, "CancelCall") == 0) return hs_cancel_ringing(conn, msg); if (strcmp(member, "Play") == 0) -- cgit From 7d487896f72233ab3bff4d8b7f5e9ca12c4b4a0b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 9 Jan 2007 13:33:13 +0000 Subject: Cleanup (get rid of some unnecessary indentation) --- audio/headset.c | 85 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 44 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 2bda011d..b1b1143f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -278,8 +278,9 @@ static void parse_headset_event(const char *buf, char *rsp, int rsp_len) static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { - int sk, ret; + int sk, ret, free_space; unsigned char buf[BUF_SIZE]; + char *cr; if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -292,63 +293,59 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ sk = g_io_channel_unix_get_fd(chan); ret = read(sk, buf, sizeof(buf) - 1); - if (ret > 0) { - int free_space; - char *cr; + if (ret <= 0) + goto failed; - free_space = sizeof(hs->buf) - hs->data_start - - hs->data_length - 1; + free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; - if (free_space < ret) { - /* Very likely that the HS is sending us garbage so - * just ignore the data and disconnect */ - error("Too much data to fit incomming buffer"); - goto failed; - } + if (free_space < ret) { + /* Very likely that the HS is sending us garbage so + * just ignore the data and disconnect */ + error("Too much data to fit incomming buffer"); + goto failed; + } - memcpy(&hs->buf[hs->data_start], buf, ret); - hs->data_length += ret; + memcpy(&hs->buf[hs->data_start], buf, ret); + hs->data_length += ret; - /* Make sure the data is null terminated so we can use string - * functions */ - hs->buf[hs->data_length] = '\0'; + /* Make sure the data is null terminated so we can use string + * functions */ + hs->buf[hs->data_length] = '\0'; - cr = strchr(&hs->buf[hs->data_start], '\r'); - if (cr) { - char rsp[BUF_SIZE]; - int len, written; - off_t cmd_len; - - cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; - *cr = '\0'; + cr = strchr(&hs->buf[hs->data_start], '\r'); + if (cr) { + char rsp[BUF_SIZE]; + int len, written; + off_t cmd_len; - memset(rsp, 0, sizeof(rsp)); + cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; + *cr = '\0'; - parse_headset_event(&hs->buf[hs->data_start], - rsp, sizeof(rsp)); + memset(rsp, 0, sizeof(rsp)); - len = strlen(rsp); - written = 0; + parse_headset_event(&hs->buf[hs->data_start], + rsp, sizeof(rsp)); - while (written < len) { - int ret; + len = strlen(rsp); + written = 0; - ret = write(sk, &rsp[written], len - written); - if (ret < 0) { - error("write: %s (%d)", - strerror(errno), errno); - break; - } + while (written < len) { + int ret; - written += ret; + ret = write(sk, &rsp[written], len - written); + if (ret < 0) { + error("write: %s (%d)", strerror(errno), errno); + break; } - hs->data_start += cmd_len; - hs->data_length -= cmd_len; - - if (!hs->data_length) - hs->data_start = 0; + written += ret; } + + hs->data_start += cmd_len; + hs->data_length -= cmd_len; + + if (!hs->data_length) + hs->data_start = 0; } if (hs->ring_timer) { -- cgit From 230f57309dce0d01fd35bdc9dac460f348ec0dab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 10 Jan 2007 10:23:51 +0000 Subject: Small fix in case hs->data_start != 0 --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b1b1143f..02938768 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -310,7 +310,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ /* Make sure the data is null terminated so we can use string * functions */ - hs->buf[hs->data_length] = '\0'; + hs->buf[hs->data_start + hs->data_length] = '\0'; cr = strchr(&hs->buf[hs->data_start], '\r'); if (cr) { -- cgit From c97480f11bdfdc3cbb867b8127074bcb6b047024 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 12 Jan 2007 00:37:42 +0000 Subject: Allow compilation against GLib --- audio/Makefile.am | 14 +++++++++++--- audio/headset.c | 8 ++++---- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 85025d5b..45afab3c 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,15 +1,23 @@ +if GLIB +glib_cflags = @GLIB_CFLAGS@ +glib_ldadd = @GLIB_LIBS@ +else +glib_cflags = +glib_ldadd = +endif + noinst_PROGRAMS = bt.audiod bt.headsetd bt_audiod_SOURCES = main.c -bt_audiod_LDADD = @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a +bt_audiod_LDADD = $(glib_ldadd) @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a bt_headsetd_SOURCES = headset.c -bt_headsetd_LDADD = @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a +bt_headsetd_LDADD = $(glib_ldadd) @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ $(glib_cflags) INCLUDES = -I$(top_srcdir)/common diff --git a/audio/headset.c b/audio/headset.c index 02938768..144d2668 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -27,12 +27,12 @@ #include #include -#include -#include -#include #include #include +#include #include +#include +#include #include #include @@ -48,7 +48,7 @@ #include "dbus.h" #include "logging.h" -#include "glib-ectomy.c" +#include "glib-ectomy.h" #define BUF_SIZE 1024 -- cgit From 467f83c3ac4e4afba959e8cfd8aadb0f1614c9fb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 15 Jan 2007 18:38:58 +0000 Subject: Preliminary support for catching the unique bus name of an execed service --- audio/headset.c | 77 +++------------------------------------------------------ 1 file changed, 4 insertions(+), 73 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 144d2668..8499781b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1040,28 +1040,6 @@ static DBusHandlerResult stop_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult release_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessage *reply; - - reply = dbus_message_new_method_return(msg); - if (!reply) { - error("Can't create reply message"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - dbus_connection_send(conn, reply, NULL); - - dbus_message_unref(reply); - - info("Got Release method. Exiting."); - - raise(SIGTERM); - - return DBUS_HANDLER_RESULT_HANDLED; -} - static DBusHandlerResult hs_message(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1075,8 +1053,6 @@ static DBusHandlerResult hs_message(DBusConnection *conn, return start_message(conn, msg, data); if (strcmp(member, "Stop") == 0) return stop_message(conn, msg, data); - if (strcmp(member, "Release") == 0) - return release_message(conn, msg, data); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1112,38 +1088,8 @@ static const DBusObjectPathVTable hs_table = { .message_function = hs_message, }; -static void register_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError derr; - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("Registering failed: %s", derr.message); - dbus_error_free(&derr); - dbus_message_unref(reply); - raise(SIGTERM); - return; - } - - debug("Successfully registered headset service"); - - dbus_message_unref(reply); - - if (config_channel) - record_id = add_ag_record(config_channel); - - if (on_init_bda) - hs_connect(NULL, NULL, on_init_bda); -} - int headset_dbus_init(char *bda) { - DBusMessage *msg; - DBusPendingCall *pending; - const char *name = "Headset service"; - const char *description = "A service for headsets"; - connection = init_dbus(NULL, NULL, NULL); if (!connection) return -1; @@ -1154,26 +1100,11 @@ int headset_dbus_init(char *bda) return -1; } - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Manager", "RegisterService"); - if (!msg) { - error("Can't allocate new method call"); - return -1; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &description, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { - error("Sending Register method call failed"); - dbus_message_unref(msg); - return -1; - } + if (config_channel) + record_id = add_ag_record(config_channel); - dbus_pending_call_set_notify(pending, register_reply, NULL, NULL); - dbus_message_unref(msg); + if (on_init_bda) + hs_connect(NULL, NULL, on_init_bda); return 0; } -- cgit From 6c3061aae25e60fd62c189b88677000a36078901 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 15 Jan 2007 20:38:41 +0000 Subject: Add example service file for bt.headsetd --- audio/headset.service | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 audio/headset.service (limited to 'audio') diff --git a/audio/headset.service b/audio/headset.service new file mode 100644 index 00000000..d77d6062 --- /dev/null +++ b/audio/headset.service @@ -0,0 +1,5 @@ +# Example service file for bt.headsetd +[Bluetooth Service] +Name=Headset service +Exec=/usr/bin/bt.headsetd -c 3 -n +Description=This is a headset service -- cgit From 67038f290a093200e0b5dfc66f1b25d12ba90578 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 16 Jan 2007 10:12:08 +0000 Subject: Include headset.service into the distribution --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 45afab3c..f7d6b50c 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -21,6 +21,6 @@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ $(glib_cflags) INCLUDES = -I$(top_srcdir)/common -EXTRA_DIST = audio-api.txt +EXTRA_DIST = headset.service audio-api.txt MAINTAINERCLEANFILES = Makefile.in -- cgit From 8af0823c55376fb62a8207e361ee49357781d4a1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 16 Jan 2007 10:13:10 +0000 Subject: Add identifier tag --- audio/headset.service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.service b/audio/headset.service index d77d6062..b86ff2fc 100644 --- a/audio/headset.service +++ b/audio/headset.service @@ -1,5 +1,5 @@ -# Example service file for bt.headsetd [Bluetooth Service] Name=Headset service Exec=/usr/bin/bt.headsetd -c 3 -n -Description=This is a headset service +Description=Bluetooth headset service +Identifier=headset -- cgit From aa79fbd0ea4a4b97e174d6835be895ead383d885 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 18 Jan 2007 21:56:35 +0000 Subject: Update audio service API description --- audio/audio-api.txt | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 9427ca71..436296a7 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -1,41 +1,53 @@ -Audio API description for BlueZ -******************************* +Bluetooth audio service API description +*************************************** -Copyright (C) 2004-2006 Marcel Holtmann +Copyright (C) 2004-2007 Marcel Holtmann Copyright (C) 2005-2006 Johan Hedberg Copyright (C) 2005-2006 Brad Midgley -Audio hierarchy -=============== +Audio Manager hierarchy +======================= Service org.bluez.audio -Interface org.bluez.Audio +Interface org.bluez.audio.Manager Object path /org/bluez/audio Methods array{string} ListHeadsets() - Returns list of Bluetooth addresses for devices that - are configured as headsets. + Returns list of headset objects that + are configured. - string GetDefaultHeadset() + string DefaultHeadset() - Returns the Bluetooth address of the default headset - device. + Returns the object path for the default + headset device. - void SetDefaultHeadset(string address) + string CreateHeadset(string address) - void CreateHeadset(string address) + Create a new headset device and returns + its object path on return. - void RemoveHeadset(string address) + void RemoveHeadset(string path) - string ConnectHeadset(string address) +Signals void HeadsetCreated(string path) - Connects to the headset and setups a new stream. The - new stream identifier is returned. + void HeadsetRemoved(string path) -Signals void HeadsetCreated(string address) + void DefaultHeadsetChanged(string path) - void HeadsetRemoved(string address) - void DefaultHeadsetChanged(string address) +Audio Headset hierarchy +======================= + +Service org.bluez.audio +Interface org.bluez.audio.Headset +Object path /org/bluez/audio/headset* + +Methods string GetAddress() + + string GetName() + + void Connect() + + void Disconnect() -- cgit From 028a55862c3a2d0cb789430ce33ac52c80147ca7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 19 Jan 2007 21:44:34 +0000 Subject: Rename services and install the audio service --- audio/Makefile.am | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index f7d6b50c..b6d39c61 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,4 +1,6 @@ +servicedir = $(libdir)/bluetooth + if GLIB glib_cflags = @GLIB_CFLAGS@ glib_ldadd = @GLIB_LIBS@ @@ -7,15 +9,16 @@ glib_cflags = glib_ldadd = endif -noinst_PROGRAMS = bt.audiod bt.headsetd +service_PROGRAMS = bluetoothd-service-audio -bt_audiod_SOURCES = main.c +noinst_PROGRAMS = bluetoothd-service-headset -bt_audiod_LDADD = $(glib_ldadd) @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a +bluetoothd_service_audio_SOURCES = main.c -bt_headsetd_SOURCES = headset.c +bluetoothd_service_headset_SOURCES = headset.c -bt_headsetd_LDADD = $(glib_ldadd) @DBUS_LIBS@ @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a +LDADD = $(top_builddir)/common/libhelper.a $(glib_ldadd) \ + @DBUS_LIBS@ @BLUEZ_LIBS@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ $(glib_cflags) -- cgit From 358fd1c1f4a820f45068b750cf001de23342f38b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 19 Jan 2007 22:14:59 +0000 Subject: No need to become a daemon --- audio/headset.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 8499781b..28d369c6 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1574,14 +1574,10 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) int main(int argc, char *argv[]) { struct sigaction sa; - int opt, daemonize = 1; + int opt; - while ((opt = getopt(argc, argv, "nc:o:i:")) != EOF) { + while ((opt = getopt(argc, argv, "c:o:i:")) != EOF) { switch (opt) { - case 'n': - daemonize = 0; - break; - case 'c': config_channel = strtol(optarg, NULL, 0); break; @@ -1605,17 +1601,10 @@ int main(int argc, char *argv[]) exit(1); } - if (argv[optind]) { + if (argv[optind]) on_init_bda = argv[optind]; - daemonize = 0; - } - - if (daemonize && daemon(0, 0)) { - error("Can't daemonize: %s (%d)", strerror(errno), errno); - exit(1); - } - start_logging("bt.headsetd", "Bluetooth Headset daemon"); + start_logging("headset", "Bluetooth Headset daemon"); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; -- cgit From 03cc48fcbe4b649e6f20c0417977d5b7e127ce06 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 19 Jan 2007 23:51:19 +0000 Subject: Use database interface for service records --- audio/headset.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 28d369c6..d5cca6bb 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -862,7 +862,7 @@ static uint32_t add_ag_record(uint8_t channel) sdp_buf_t buf; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Manager", "AddServiceRecord"); + "org.bluez.Database", "AddServiceRecord"); if (!msg) { error("Can't allocate new method call"); return 0; @@ -874,9 +874,8 @@ static uint32_t add_ag_record(uint8_t channel) return 0; } - dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &buf.data, buf.data_size, - DBUS_TYPE_INVALID); + dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &buf.data, buf.data_size, DBUS_TYPE_INVALID); dbus_error_init(&derr); reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); @@ -913,15 +912,14 @@ static int remove_ag_record(uint32_t rec_id) DBusError derr; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Manager", "RemoveServiceRecord"); + "org.bluez.Database", "RemoveServiceRecord"); if (!msg) { error("Can't allocate new method call"); return 0; } - dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_path, - DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); dbus_error_init(&derr); reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); -- cgit From 968c780c4687c06a29cd52eac140e1f0ce100d79 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 19 Jan 2007 23:56:06 +0000 Subject: Use channel 12 as default --- audio/headset.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index d5cca6bb..14c8c149 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -83,7 +83,7 @@ struct headset { static struct pending_connect *connect_in_progress = NULL; -static uint8_t config_channel = 0; +static uint8_t config_channel = 12; static uint32_t record_id = 0; @@ -1594,11 +1594,6 @@ int main(int argc, char *argv[]) } } - if (!config_channel) { - printf("You need to supply a local channel with the -c switch\n"); - exit(1); - } - if (argv[optind]) on_init_bda = argv[optind]; -- cgit From 157437dcad4dbd248b129ef9e9f7b3dddf0a1d83 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Jan 2007 00:00:23 +0000 Subject: Remove service agent relict --- audio/headset.c | 82 ++------------------------------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 14c8c149..220562fd 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -89,8 +89,6 @@ static uint32_t record_id = 0; static char *on_init_bda = NULL; -static int started = 0; - static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; @@ -968,76 +966,6 @@ static void create_server_socket(void) g_io_add_watch(server_sk, G_IO_IN, (GIOFunc) server_io_cb, NULL); } - -static DBusHandlerResult start_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessage *reply; - - info("Starting headset service"); - - reply = dbus_message_new_method_return(msg); - if (!reply) { - error("Can't create reply message"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - create_server_socket(); - - dbus_connection_send(conn, reply, NULL); - - dbus_message_unref(reply); - - started = 1; - - return DBUS_HANDLER_RESULT_HANDLED; -} - -static DBusHandlerResult stop_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessage *reply; - - info("Stopping headset service"); - - reply = dbus_message_new_method_return(msg); - if (!reply) { - error("Can't create reply message"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - dbus_connection_send(conn, reply, NULL); - - dbus_message_unref(reply); - - if (hs) { - if (hs->sco) - g_io_channel_close(hs->sco); - if (hs->rfcomm) - g_io_channel_close(hs->rfcomm); - if (hs->out >= 0) { - close(hs->out); - hs->out = -1; - } - free(hs); - hs = NULL; - } - - if (!config_channel && record_id) { - remove_ag_record(record_id); - record_id = 0; - } - - if (server_sk) { - g_io_channel_close(server_sk); - server_sk = NULL; - } - - started = 0; - - return DBUS_HANDLER_RESULT_HANDLED; -} - static DBusHandlerResult hs_message(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1046,14 +974,6 @@ static DBusHandlerResult hs_message(DBusConnection *conn, interface = dbus_message_get_interface(msg); member = dbus_message_get_member(msg); - if (strcmp(interface, "org.bluez.ServiceAgent") == 0) { - if (strcmp(member, "Start") == 0) - return start_message(conn, msg, data); - if (strcmp(member, "Stop") == 0) - return stop_message(conn, msg, data); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && !strcmp("Introspect", member)) return simple_introspect(conn, msg, data); @@ -1618,6 +1538,8 @@ int main(int argc, char *argv[]) exit(1); } + create_server_socket(); + g_main_run(main_loop); return 0; -- cgit From 884cfe825db1a0e8c98aa6a604fd4588a1de1340 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Jan 2007 02:36:57 +0000 Subject: Don't install services that are in experimental state --- audio/Makefile.am | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index b6d39c61..60a546e7 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -9,9 +9,8 @@ glib_cflags = glib_ldadd = endif -service_PROGRAMS = bluetoothd-service-audio - -noinst_PROGRAMS = bluetoothd-service-headset +noinst_PROGRAMS = bluetoothd-service-audio \ + bluetoothd-service-headset bluetoothd_service_audio_SOURCES = main.c -- cgit From 8cc5595d9091b484b9a4abe314c0f3ec055e0581 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Jan 2007 05:26:15 +0000 Subject: Make it possible to support an embedded GLib --- audio/Makefile.am | 14 +++----------- audio/headset.c | 13 +++++++------ 2 files changed, 10 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 60a546e7..f7e48a3f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,14 +1,6 @@ servicedir = $(libdir)/bluetooth -if GLIB -glib_cflags = @GLIB_CFLAGS@ -glib_ldadd = @GLIB_LIBS@ -else -glib_cflags = -glib_ldadd = -endif - noinst_PROGRAMS = bluetoothd-service-audio \ bluetoothd-service-headset @@ -16,10 +8,10 @@ bluetoothd_service_audio_SOURCES = main.c bluetoothd_service_headset_SOURCES = headset.c -LDADD = $(top_builddir)/common/libhelper.a $(glib_ldadd) \ - @DBUS_LIBS@ @BLUEZ_LIBS@ +LDADD = $(top_builddir)/common/libhelper.a \ + @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ $(glib_cflags) +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ INCLUDES = -I$(top_srcdir)/common diff --git a/audio/headset.c b/audio/headset.c index 220562fd..0be529ec 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -44,11 +44,12 @@ #include #include +#include + #include #include "dbus.h" #include "logging.h" -#include "glib-ectomy.h" #define BUF_SIZE 1024 @@ -347,7 +348,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ } if (hs->ring_timer) { - g_timeout_remove(hs->ring_timer); + g_source_remove(hs->ring_timer); hs->ring_timer = 0; } @@ -752,7 +753,7 @@ failed: static void sig_term(int sig) { - g_main_quit(main_loop); + g_main_loop_quit(main_loop); } static int server_socket(uint8_t *channel) @@ -1371,7 +1372,7 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms goto done; } - g_timeout_remove(hs->ring_timer); + g_source_remove(hs->ring_timer); hs->ring_timer = 0; done: @@ -1531,7 +1532,7 @@ int main(int argc, char *argv[]) enable_debug(); - main_loop = g_main_new(FALSE); + main_loop = g_main_loop_new(NULL, FALSE); if (headset_dbus_init(NULL) < 0) { error("Unable to get on D-Bus"); @@ -1540,7 +1541,7 @@ int main(int argc, char *argv[]) create_server_socket(); - g_main_run(main_loop); + g_main_loop_run(main_loop); return 0; } -- cgit From 0bf515081f1ddded1757cefe82651e2ca686ee35 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 20 Jan 2007 15:07:31 +0000 Subject: Update service description file --- audio/headset.service | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/headset.service b/audio/headset.service index b86ff2fc..b2f043ee 100644 --- a/audio/headset.service +++ b/audio/headset.service @@ -1,5 +1,4 @@ [Bluetooth Service] -Name=Headset service -Exec=/usr/bin/bt.headsetd -c 3 -n -Description=Bluetooth headset service Identifier=headset +Name=Headset service +Description=Bluetooth Headset and Handsfree service -- cgit From b7506049ed9fc1704b2b422af3dedd22f8a5b066 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 21 Jan 2007 21:01:01 +0000 Subject: Fix setup routine for headset --- audio/headset.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0be529ec..c5d17315 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -964,7 +964,11 @@ static void create_server_socket(void) return; } + g_io_channel_set_close_on_unref(server_sk, TRUE); + g_io_add_watch(server_sk, G_IO_IN, (GIOFunc) server_io_cb, NULL); + + g_io_channel_unref(server_sk); } static DBusHandlerResult hs_message(DBusConnection *conn, @@ -1007,13 +1011,9 @@ static const DBusObjectPathVTable hs_table = { .message_function = hs_message, }; -int headset_dbus_init(char *bda) +int setup_dbus(DBusConnection *conn) { - connection = init_dbus(NULL, NULL, NULL); - if (!connection) - return -1; - - if (!dbus_connection_register_object_path(connection, hs_path, + if (!dbus_connection_register_object_path(conn, hs_path, &hs_table, NULL)) { error("D-Bus failed to register %s path", hs_path); return -1; @@ -1492,6 +1492,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) int main(int argc, char *argv[]) { + DBusConnection *system_bus; struct sigaction sa; int opt; @@ -1534,8 +1535,17 @@ int main(int argc, char *argv[]) main_loop = g_main_loop_new(NULL, FALSE); - if (headset_dbus_init(NULL) < 0) { - error("Unable to get on D-Bus"); + system_bus = init_dbus(NULL, NULL, NULL); + if (!system_bus) { + error("Connection to system bus failed"); + g_main_loop_unref(main_loop); + exit(1); + } + + if (setup_dbus(system_bus) < 0) { + error("Connection setup failed"); + dbus_connection_unref(system_bus); + g_main_loop_unref(main_loop); exit(1); } @@ -1543,5 +1553,9 @@ int main(int argc, char *argv[]) g_main_loop_run(main_loop); + dbus_connection_unref(system_bus); + + g_main_loop_unref(main_loop); + return 0; } -- cgit From 82a96ac9b3a4f4f08eadd26d6774b35609fecbbd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 21 Jan 2007 21:06:24 +0000 Subject: Cleanup logging on exit --- audio/headset.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c5d17315..9a7daef0 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1557,5 +1557,9 @@ int main(int argc, char *argv[]) g_main_loop_unref(main_loop); + info("Exit"); + + stop_logging(); + return 0; } -- cgit From e59b7c56d974614b47f287177c275166e1e484c8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 21 Jan 2007 21:57:13 +0000 Subject: Add workaround for the global connection variable --- audio/headset.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 9a7daef0..728d38d2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1542,6 +1542,8 @@ int main(int argc, char *argv[]) exit(1); } + connection = system_bus; + if (setup_dbus(system_bus) < 0) { error("Connection setup failed"); dbus_connection_unref(system_bus); -- cgit From 7f496bf581e831e8b256ce9b0447a13a882fdad3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 23 Jan 2007 20:34:45 +0000 Subject: rework of the headset daemon code --- audio/headset.c | 1075 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 662 insertions(+), 413 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 728d38d2..78d13092 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -55,59 +56,77 @@ #define RING_INTERVAL 3000 -#define HEADSET_PATH "/org/bluez/headset" -static const char *hs_path = HEADSET_PATH; +#define AUDIO_MANAGER_PATH "/org/bluez/audio" +#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" struct pending_connect { - bdaddr_t bda; int ch; DBusConnection *conn; DBusMessage *msg; GIOChannel *io; }; +typedef enum { + HEADSET_STATE_DISCONNECTED, + HEADSET_STATE_CONNECT_IN_PROGRESS, + HEADSET_STATE_CONNECTED, + HEADSET_STATE_PLAY_IN_PROGRESS, + HEADSET_STATE_PLAYING, +} headset_state_t; + struct headset { - char address[18]; + char object_path[128]; + bdaddr_t bda; GIOChannel *rfcomm; GIOChannel *sco; + char *input; GIOChannel *audio_input; - int out; + char *output; + GIOChannel *audio_output; guint ring_timer; char buf[BUF_SIZE]; int data_start; int data_length; -}; - -static struct pending_connect *connect_in_progress = NULL; -static uint8_t config_channel = 12; + headset_state_t state; + struct pending_connect *connect_in_progress; + uint32_t record_id; + GIOChannel *server_sk; +}; -static uint32_t record_id = 0; +struct manager { + GSList *headset_list; +}; -static char *on_init_bda = NULL; static DBusConnection *connection = NULL; - static GMainLoop *main_loop = NULL; -static struct headset *hs = NULL; - -static GIOChannel *server_sk = NULL; - -static char *audio_input = NULL; -static char *audio_output = NULL; - -static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, - const char *address); -static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg); -static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg); -static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg); -static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg); -static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg); +struct manager* audio_manager_new(DBusConnection *conn); +void audio_manager_add_headset(struct manager *amanager, struct headset *hs); +static DBusHandlerResult am_default_headset(struct manager *amanager, DBusMessage *msg); +static DBusHandlerResult am_connect(struct manager *amanager, DBusMessage *msg); + +struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel); +int audio_headset_close_input(struct headset* hs); +int audio_headset_open_input(struct headset* hs, const char *audio_input); +int audio_headset_close_output(struct headset* hs); +int audio_headset_open_output(struct headset* hs, const char *audio_output); +void audio_headset_create_server_socket(struct headset *hs, uint8_t chan); +int audio_headset_send_ring(struct headset *hs); + +static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg); +static void hs_signal(struct headset *hs, const char *name); +static void hs_signal_gain_setting(struct headset *hs, const char *buf); static int set_nonblocking(int fd, int *err) { @@ -146,8 +165,6 @@ static void pending_connect_free(struct pending_connect *c, gboolean unref_io) if (c->conn) dbus_connection_unref(c->conn); free(c); - - connect_in_progress = NULL; } static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, @@ -203,7 +220,7 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); } -static void send_gain_setting(const char *buf) +static void hs_signal_gain_setting(struct headset *hs, const char *buf) { const char *name; DBusMessage *signal; @@ -226,7 +243,7 @@ static void send_gain_setting(const char *buf) return; } - signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name); + signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name); if (!signal) { error("Unable to allocate new GainChanged signal"); return; @@ -241,11 +258,11 @@ static void send_gain_setting(const char *buf) dbus_message_unref(signal); } -static void send_simple_signal(const char *name) +static void hs_signal(struct headset *hs, const char *name) { DBusMessage *signal; - signal = dbus_message_new_signal(HEADSET_PATH, "org.bluez.Headset", name); + signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name); if (!signal) { error("Unable to allocate new AnswerRequested signal"); return; @@ -255,28 +272,34 @@ static void send_simple_signal(const char *name) dbus_message_unref(signal); } -static void parse_headset_event(const char *buf, char *rsp, int rsp_len) +static int parse_headset_event(const char *buf, char *rsp, int rsp_len) { + int rv = 0; + printf("Received: %s\n", buf); /* Return an error if this is not a proper AT command */ if (strncmp(buf, "AT", 2)) { snprintf(rsp, rsp_len, "\r\nERROR\r\n"); - return; + return rv; } buf += 2; if (!strncmp(buf, "+CKPD", 5)) - send_simple_signal("AnswerRequested"); + rv = 0; else if (!strncmp(buf, "+VG", 3)) - send_gain_setting(buf); + rv = 1; snprintf(rsp, rsp_len, "\r\nOK\r\n"); + + /* return 1 if gain event */ + return rv; } -static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { + struct headset *hs = (struct headset *)data; int sk, ret, free_space; unsigned char buf[BUF_SIZE]; char *cr; @@ -322,8 +345,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ memset(rsp, 0, sizeof(rsp)); - parse_headset_event(&hs->buf[hs->data_start], - rsp, sizeof(rsp)); + if (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp)) == 1) + hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); + else + hs_signal(hs, "AnswerRequested"); len = strlen(rsp); written = 0; @@ -355,15 +380,19 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_ return TRUE; failed: - info("Disconnected from %s", hs->address); - send_simple_signal("Disconnected"); - if (hs->sco) + info("Disconnected from %s", hs->object_path); + hs_signal(hs, "Disconnected"); + if (hs->sco) { g_io_channel_close(hs->sco); - if (hs->out >= 0) - close(hs->out); + hs->sco = NULL; + } + if (hs->audio_output) { + g_io_channel_close(hs->audio_output); + hs->audio_output = NULL; + } g_io_channel_close(chan); - free(hs); - hs = NULL; + hs->rfcomm = NULL; + return FALSE; } @@ -372,6 +401,10 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) int srv_sk, cli_sk; struct sockaddr_rc addr; socklen_t size; + char hs_address[18]; + struct headset *hs = (struct headset *)data; + + assert(hs != NULL); if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -381,7 +414,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) if (cond & (G_IO_HUP | G_IO_ERR)) { error("Hangup or error on rfcomm server socket"); g_io_channel_close(chan); - server_sk = NULL; + hs->server_sk = NULL; return TRUE; } @@ -394,125 +427,72 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (hs || connect_in_progress) { + if (hs->state > HEADSET_STATE_DISCONNECTED) { debug("Refusing new connection since one already exists"); close(cli_sk); return TRUE; } - hs = malloc(sizeof(struct headset)); - if (!hs) { - error("Allocating new hs connection struct failed!"); - close(cli_sk); - return TRUE; - } - - memset(hs, 0, sizeof(struct headset)); - - hs->out = -1; - hs->rfcomm = g_io_channel_unix_new(cli_sk); if (!hs->rfcomm) { error("Allocating new GIOChannel failed!"); close(cli_sk); - free(hs); - hs = NULL; return TRUE; } - ba2str(&addr.rc_bdaddr, hs->address); + ba2str(&addr.rc_bdaddr, hs_address); - debug("Accepted connection from %s", hs->address); + debug("Accepted connection from %s, %s", hs_address, hs->object_path); - send_simple_signal("Connected"); + hs_signal(hs, "Connected"); - g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, - hs); + g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); return TRUE; } -static gboolean audio_input_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - int in, out, data_size, written; + struct headset *hs = (struct headset *)data; char buf[1024]; + gsize bytes_read; + gsize bytes_written, total_bytes_written; + GIOError err; if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); - hs->audio_input = NULL; + audio_headset_close_input(hs); return FALSE; } if (cond & (G_IO_HUP | G_IO_ERR)) { - g_io_channel_close(hs->audio_input); - hs->audio_input = NULL; - if (hs->out >= 0) { - close(hs->out); - hs->out = -1; - } + audio_headset_close_input(hs); return FALSE; } - in = g_io_channel_unix_get_fd(chan); - out = g_io_channel_unix_get_fd(hs->sco); - - data_size = read(in, buf, sizeof(buf)); - if (data_size < 0) { - error("read: %s (%d)", strerror(errno), errno); - g_io_channel_close(chan); - hs->audio_input = NULL; - return TRUE; - } - - /* EOF */ - if (data_size == 0) { - debug("Reached end of file"); - g_io_channel_close(chan); - hs->audio_input = NULL; - return TRUE; - } - - written = 0; - - while (written < data_size) { - int ret; - - ret = write(out, &buf[written], data_size - written); - - if (ret < 0) { - error("write(%d, %p, %d): %s (%d)", out, &buf[data_size], - data_size - written, strerror(errno), errno); - g_io_channel_close(chan); - hs->audio_input = NULL; - return TRUE; - } - - debug("wrote %d bytes to %s", ret, audio_output); - - written += ret; - } + err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); + if (err != G_IO_ERROR_NONE) + return FALSE; + + total_bytes_written = bytes_written = 0; + do { + /* FIXME: make it async */ + err = g_io_channel_write(hs->sco, buf, bytes_read, &bytes_written); + total_bytes_written += bytes_written; + } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read); return TRUE; } -static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - int in, ret; + struct headset *hs = (struct headset *)data; char buf[1024]; + gsize bytes_read; + gsize bytes_written, total_bytes_written; + GIOError err; if (cond & G_IO_NVAL) { g_io_channel_unref(chan); - if (hs) { - if (hs->audio_input) { - g_io_channel_close(hs->audio_input); - hs->audio_input = NULL; - } - if (hs->out >= 0) { - close(hs->out); - hs->out = -1; - } - } - return FALSE; } @@ -524,34 +504,36 @@ static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, gpointer user_dat g_io_channel_close(hs->audio_input); hs->audio_input = NULL; } - send_simple_signal("Stopped"); + hs_signal(hs, "Stopped"); return FALSE; } - if (!audio_output) { - debug("sco_io_cb: Unhandled IO condition"); - return TRUE; - } - - in = g_io_channel_unix_get_fd(chan); - if (hs->out < 0) - hs->out = open(audio_output, O_WRONLY | O_SYNC | O_CREAT); + if (!hs->audio_output && hs->output) + audio_headset_open_output(hs, hs->output); - if (hs->out < 0) { - error("open(%s): %s (%d)", audio_output, strerror(errno), errno); + if (!hs->audio_output) { + error("no audio output"); g_io_channel_close(chan); + hs->sco = NULL; return TRUE; } - ret = read(in, buf, sizeof(buf)); - if (ret > 0) - ret = write(hs->out, buf, ret); + err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); + if (err != G_IO_ERROR_NONE) + return FALSE; + + total_bytes_written = bytes_written = 0; + do { + /* FIXME: make it async */ + err = g_io_channel_write(hs->audio_output, buf, bytes_read, &bytes_written); + total_bytes_written += bytes_written; + } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read); return TRUE; } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - struct pending_connect *c) + struct headset *hs) { int ret, sk, err, flags; DBusMessage *reply; @@ -579,52 +561,50 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, debug("SCO socket %d opened", sk); - if (audio_output) - flags = G_IO_IN; - else - flags = 0; + flags = hs->audio_output ? G_IO_IN : 0; hs->sco = chan; - g_io_add_watch(chan, flags, sco_io_cb, NULL); + g_io_add_watch(chan, flags, sco_input_to_audio_output_cb, NULL); - reply = dbus_message_new_method_return(c->msg); - if (reply) { - dbus_connection_send(c->conn, reply, NULL); - dbus_message_unref(reply); - } - - if (audio_input) { - int in; - - in = open(audio_input, O_RDONLY | O_NOCTTY); - - if (in < 0) - error("open(%s): %s %d", audio_input, strerror(errno), errno); - else { - hs->audio_input = g_io_channel_unix_new(in); - g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_cb, NULL); + if (hs->connect_in_progress->msg) { + reply = dbus_message_new_method_return(hs->connect_in_progress->msg); + if (reply) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); } - } - pending_connect_free(c, FALSE); + if (hs->audio_input) + g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs); - send_simple_signal("Playing"); + pending_connect_free(hs->connect_in_progress, FALSE); + hs->connect_in_progress = NULL; + + hs->state = HEADSET_STATE_PLAYING; + hs_signal(hs, "Playing"); return FALSE; failed: - err_connect_failed(c->conn, c->msg, err); - pending_connect_free(c, TRUE); + if (hs->connect_in_progress) { + err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); + pending_connect_free(hs->connect_in_progress, TRUE); + hs->connect_in_progress = NULL; + } return FALSE; } -static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) +static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) { + char hs_address[18]; int sk, ret, err; socklen_t len; - + + if (!hs->connect_in_progress) { + error("connect in progress is null in rfcomm_connect_cb!"); + return FALSE; + } if (cond & G_IO_NVAL) { g_io_channel_unref(chan); return FALSE; @@ -645,56 +625,52 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pe goto failed; } - hs = malloc(sizeof(struct headset)); - if (!hs) { - err = ENOMEM; - error("Allocating new hs connection struct failed!"); - goto failed; - } - - memset(hs, 0, sizeof(struct headset)); - - hs->out = -1; - - ba2str(&c->bda, hs->address); + ba2str(&hs->bda, hs_address); hs->rfcomm = chan; - send_simple_signal("Connected"); + hs->state = HEADSET_STATE_CONNECTED; + hs_signal(hs, "Connected"); - debug("Connected to %s", hs->address); + debug("Connected to %s", hs_address); g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); - if (c->msg) { + if (hs->connect_in_progress->msg) { DBusMessage *reply; - reply = dbus_message_new_method_return(c->msg); + reply = dbus_message_new_method_return(hs->connect_in_progress->msg); if (reply) { - dbus_connection_send(c->conn, reply, NULL); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } + pending_connect_free(hs->connect_in_progress, FALSE); + hs->connect_in_progress = NULL; } - pending_connect_free(c, FALSE); - return FALSE; failed: - err_connect_failed(c->conn, c->msg, err); - pending_connect_free(c, TRUE); + if (hs->connect_in_progress) { + err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); + pending_connect_free(hs->connect_in_progress, TRUE); + hs->connect_in_progress = NULL; + } + hs->state = HEADSET_STATE_DISCONNECTED; return FALSE; } -static int rfcomm_connect(struct pending_connect *c, int *err) +static int rfcomm_connect(struct headset *hs, int *err) { struct sockaddr_rc addr; char address[18]; int sk; - ba2str(&c->bda, address); + assert(hs != NULL && hs->connect_in_progress != NULL); + + ba2str(&hs->bda, address); - debug("Connecting to %s channel %d", address, c->ch); + debug("Connecting to %s channel %d", address, hs->connect_in_progress->ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { @@ -721,11 +697,15 @@ static int rfcomm_connect(struct pending_connect *c, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &c->bda); - addr.rc_channel = c->ch; + bacpy(&addr.rc_bdaddr, &hs->bda); + addr.rc_channel = hs->connect_in_progress->ch; - c->io = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(c->io, TRUE); + hs->connect_in_progress->io = g_io_channel_unix_new(sk); + if (!hs->connect_in_progress->io) { + error("channel_unix_new failed in rfcomm connect"); + goto failed; + } + g_io_channel_set_close_on_unref(hs->connect_in_progress->io, TRUE); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -737,25 +717,25 @@ static int rfcomm_connect(struct pending_connect *c, int *err) debug("Connect in progress"); - g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, c); + g_io_add_watch(hs->connect_in_progress->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, hs); } else { debug("Connect succeeded with first try"); - rfcomm_connect_cb(c->io, G_IO_OUT, c); + rfcomm_connect_cb(hs->connect_in_progress->io, G_IO_OUT, hs); } return 0; failed: - if (sk >= 0) - close(sk); + if (hs->connect_in_progress->io) { + g_io_channel_close(hs->connect_in_progress->io); + hs->connect_in_progress->io = NULL; + } else { + if (sk >= 0) + close(sk); + } return -1; } -static void sig_term(int sig) -{ - g_main_loop_quit(main_loop); -} - static int server_socket(uint8_t *channel) { int sock; @@ -936,98 +916,6 @@ static int remove_ag_record(uint32_t rec_id) return 0; } -static void create_server_socket(void) -{ - uint8_t chan = config_channel; - int srv_sk; - - srv_sk = server_socket(&chan); - if (srv_sk < 0) { - error("Unable to create server socket"); - return; - } - - if (!record_id) - record_id = add_ag_record(chan); - - if (!record_id) { - error("Unable to register service record"); - close(srv_sk); - return; - } - - server_sk = g_io_channel_unix_new(srv_sk); - if (!server_sk) { - error("Unable to allocate new GIOChannel"); - remove_ag_record(record_id); - record_id = 0; - return; - } - - g_io_channel_set_close_on_unref(server_sk, TRUE); - - g_io_add_watch(server_sk, G_IO_IN, (GIOFunc) server_io_cb, NULL); - - g_io_channel_unref(server_sk); -} - -static DBusHandlerResult hs_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *interface, *member; - - interface = dbus_message_get_interface(msg); - member = dbus_message_get_member(msg); - - if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && - !strcmp("Introspect", member)) - return simple_introspect(conn, msg, data); - - if (strcmp(interface, "org.bluez.Headset") != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(member, "ConnectHeadset") == 0) - return hs_connect(conn, msg, NULL); - - if (strcmp(member, "Disconnect") == 0) - return hs_disconnect(conn, msg); - - if (strcmp(member, "IndicateCall") == 0) - return hs_ring(conn, msg); - - if (strcmp(member, "CancelCall") == 0) - return hs_cancel_ringing(conn, msg); - - if (strcmp(member, "Play") == 0) - return hs_play(conn, msg); - - if (strcmp(member, "Stop") == 0) - return hs_stop(conn, msg); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable hs_table = { - .message_function = hs_message, -}; - -int setup_dbus(DBusConnection *conn) -{ - if (!dbus_connection_register_object_path(conn, hs_path, - &hs_table, NULL)) { - error("D-Bus failed to register %s path", hs_path); - return -1; - } - - if (config_channel) - record_id = add_ag_record(config_channel); - - if (on_init_bda) - hs_connect(NULL, NULL, on_init_bda); - - return 0; -} - static void record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; @@ -1036,14 +924,19 @@ static void record_reply(DBusPendingCall *call, void *data) int array_len, record_len, err = EIO; sdp_record_t *record = NULL; sdp_list_t *protos; - struct pending_connect *c = data; + struct headset *hs = data; + struct pending_connect *c; + + assert (hs != NULL && hs->connect_in_progress); + c = hs->connect_in_progress; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); dbus_error_free(&derr); goto failed; } @@ -1054,14 +947,16 @@ static void record_reply(DBusPendingCall *call, void *data) if (!array) { error("Unable to get handle array from reply"); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); goto failed; } record = sdp_extract_pdu(array, &record_len); if (!record) { error("Unable to extract service record from reply"); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); goto failed; } @@ -1077,13 +972,15 @@ static void record_reply(DBusPendingCall *call, void *data) if (c->ch == -1) { error("Unable to extract RFCOMM channel from service record"); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); goto failed; } - if (rfcomm_connect(c, &err) < 0) { + if (rfcomm_connect(hs, &err) < 0) { error("Unable to connect"); - err_connect_failed(c->conn, c->msg, err); + if (c->msg) + err_connect_failed(c->conn, c->msg, err); goto failed; } @@ -1096,7 +993,8 @@ failed: if (record) sdp_record_free(record); dbus_message_unref(reply); - pending_connect_free(c, TRUE); + pending_connect_free(hs->connect_in_progress, TRUE); + hs->connect_in_progress = NULL; } static void handles_reply(DBusPendingCall *call, void *data) @@ -1104,21 +1002,27 @@ static void handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - struct pending_connect *c = data; + struct headset *hs = data; + struct pending_connect *c; char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; dbus_uint32_t handle; int array_len; + assert (hs != NULL && hs->connect_in_progress); + c = hs->connect_in_progress; + reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); - if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed")) - err_connect_failed(c->conn, c->msg, EHOSTDOWN); - else - err_not_supported(c->conn, c->msg); + if (c->msg) { + if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed")) + err_connect_failed(c->conn, c->msg, EHOSTDOWN); + else + err_not_supported(c->conn, c->msg); + } dbus_error_free(&derr); goto failed; } @@ -1129,13 +1033,15 @@ static void handles_reply(DBusPendingCall *call, void *data) if (!array) { error("Unable to get handle array from reply"); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); - err_not_supported(c->conn, c->msg); + if (c->msg) + err_not_supported(c->conn, c->msg); goto failed; } @@ -1147,11 +1053,12 @@ static void handles_reply(DBusPendingCall *call, void *data) "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - err_connect_failed(c->conn, c->msg, ENOMEM); + if (c->msg) + err_connect_failed(c->conn, c->msg, ENOMEM); goto failed; } - ba2str(&c->bda, address); + ba2str(&hs->bda, address); handle = array[0]; @@ -1161,7 +1068,8 @@ static void handles_reply(DBusPendingCall *call, void *data) if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); - err_connect_failed(c->conn, c->msg, EIO); + if (c->msg) + err_connect_failed(c->conn, c->msg, EIO); goto failed; } @@ -1179,11 +1087,12 @@ failed: pending_connect_free(c, TRUE); } -static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) +static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) { DBusError derr; DBusMessage *reply; const char *address; + char hs_address[18]; dbus_error_init(&derr); @@ -1192,113 +1101,120 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg) DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { - err_invalid_args(conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } - if (!hs || strcasecmp(address, hs->address) != 0) - return err_not_connected(conn, msg); + if (!hs || strcasecmp(address, hs_address) != 0) + return err_not_connected(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->sco) + if (hs->sco) { g_io_channel_close(hs->sco); - if (hs->out >= 0) { - close(hs->out); - hs->out = -1; + hs->sco = NULL; } - if (hs->rfcomm) - g_io_channel_close(hs->rfcomm); - info("Disconnected from %s", hs->address); + audio_headset_close_output(hs); + + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + hs->rfcomm = NULL; + } - send_simple_signal("Disconnected"); + info("Disconnected from %s", hs_address); - free(hs); - hs = NULL; + hs_signal(hs, "Disconnected"); - dbus_connection_send(conn, reply, NULL); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, - const char *address) +static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) { DBusPendingCall *pending; - struct pending_connect *c; const char *hs_svc = "hsp"; + char hs_address[18]; - if (!address) { - DBusError derr; + assert (hs != NULL); - dbus_error_init(&derr); + /* this block should be move to the manager Connect */ +/* if (!address && msg && conn) { */ +/* DBusError derr; */ - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); +/* dbus_error_init(&derr); */ - if (dbus_error_is_set(&derr)) { - err_invalid_args(conn, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - } +/* dbus_message_get_args(msg, &derr, */ +/* DBUS_TYPE_STRING, &address, */ +/* DBUS_TYPE_INVALID); */ + +/* if (dbus_error_is_set(&derr)) { */ +/* err_invalid_args(conn, msg, derr.message); */ +/* dbus_error_free(&derr); */ +/* return DBUS_HANDLER_RESULT_HANDLED; */ +/* } */ +/* } */ - if (hs) - return err_already_connected(conn, msg); + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->connect_in_progress) { + error("Already connected"); + return DBUS_HANDLER_RESULT_HANDLED; + } - c = malloc(sizeof(struct pending_connect)); - if (!c) { + hs->connect_in_progress = malloc(sizeof(struct pending_connect)); + if (!hs->connect_in_progress) { error("Out of memory when allocating new struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } - connect_in_progress = c; - - memset(c, 0, sizeof(struct pending_connect)); - - str2ba(address, &c->bda); - - c->conn = dbus_connection_ref(conn); - c->msg = dbus_message_ref(msg); + memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); + hs->connect_in_progress->conn = dbus_connection_ref(connection); + hs->connect_in_progress->msg = msg ? dbus_message_ref(msg) : NULL; msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { - pending_connect_free(c, TRUE); + pending_connect_free(hs->connect_in_progress, TRUE); + hs->connect_in_progress = NULL; return DBUS_HANDLER_RESULT_NEED_MEMORY; } - dbus_message_append_args(msg, DBUS_TYPE_STRING, &address, + ba2str(&hs->bda, hs_address); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_address, DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); - pending_connect_free(c, TRUE); + pending_connect_free(hs->connect_in_progress, TRUE); + hs->connect_in_progress = NULL; dbus_message_unref(msg); return err_connect_failed(connection, msg, EIO); } - dbus_pending_call_set_notify(pending, handles_reply, c, NULL); + dbus_pending_call_set_notify(pending, handles_reply, hs->connect_in_progress, NULL); dbus_message_unref(msg); return DBUS_HANDLER_RESULT_HANDLED;; } -static int send_ring(GIOChannel *io) +int audio_headset_send_ring(struct headset *hs) { const char *ring_str = "\r\nRING\r\n"; int sk, written, len; + assert (hs != NULL); + if (!hs->rfcomm) { + error("the headset %s is not connected", hs->object_path); + } + sk = g_io_channel_unix_get_fd(hs->rfcomm); len = strlen(ring_str); @@ -1318,20 +1234,26 @@ static int send_ring(GIOChannel *io) return 0; } -static gboolean ring_timer(gpointer user_data) +static gboolean ring_timer(gpointer data) { - if (send_ring(hs->rfcomm) < 0) + struct headset *hs = data; + + assert(hs != NULL); + + if (audio_headset_send_ring(hs) < 0) error("Sending RING failed"); return TRUE; } -static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) +static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) { DBusMessage *reply; - if (!hs) - return err_not_connected(conn, msg); + assert(hs != NULL); + + if (hs->state < HEADSET_STATE_CONNECTED) + return err_not_connected(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1342,26 +1264,26 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg) goto done; } - if (send_ring(hs->rfcomm) < 0) { + if (audio_headset_send_ring(hs) < 0) { dbus_message_unref(reply); - return err_failed(conn, msg); + return err_failed(connection, msg); } - hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, NULL); + hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, hs); done: - dbus_connection_send(conn, reply, NULL); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg) +static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) { DBusMessage *reply; if (!hs) - return err_not_connected(conn, msg); + return err_not_connected(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1376,38 +1298,43 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *ms hs->ring_timer = 0; done: - dbus_connection_send(conn, reply, NULL); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg) +static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) { struct sockaddr_sco addr; struct pending_connect *c; + char hs_address[18]; int sk, err; if (!hs) - return err_not_connected(conn, msg); + return err_not_connected(connection, msg); + + if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) + return err_already_connected(connection, msg); /* FIXME: in progress error? */ if (hs->sco) - return err_already_connected(conn, msg); + return err_already_connected(connection, msg); - c = malloc(sizeof(struct pending_connect)); - if (!c) + hs->connect_in_progress = malloc(sizeof(struct pending_connect)); + if (!hs->connect_in_progress) return DBUS_HANDLER_RESULT_NEED_MEMORY; - memset(c, 0, sizeof(struct pending_connect)); + memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); - c->conn = dbus_connection_ref(conn); + c = hs->connect_in_progress; + c->conn = dbus_connection_ref(connection); c->msg = dbus_message_ref(msg); sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } @@ -1426,18 +1353,18 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg) if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } if (set_nonblocking(sk, &err) < 0) { - err_connect_failed(conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - str2ba(hs->address, &addr.sco_bdaddr); + str2ba(hs_address, &addr.sco_bdaddr); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -1451,7 +1378,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg) g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c); } else { debug("Connect succeeded with first try"); - sco_connect_cb(c->io, G_IO_OUT, c); + sco_connect_cb(c->io, G_IO_OUT, hs); } return 0; @@ -1463,12 +1390,12 @@ failed: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) +static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) { DBusMessage *reply; if (!hs || !hs->sco) - return err_not_connected(conn, msg); + return err_not_connected(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1477,49 +1404,358 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg) g_io_channel_close(hs->sco); hs->sco = NULL; - if (hs->out >= 0) { - close(hs->out); - hs->out = -1; + hs_signal(hs, "Stopped"); + hs->state = HEADSET_STATE_CONNECTED; + + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +void audio_headset_create_server_socket(struct headset *hs, uint8_t chan) +{ + int srv_sk; + + assert (hs != NULL); + + if (hs->server_sk) { + error("Server socket already created"); + return; + } + + srv_sk = server_socket(&chan); + if (srv_sk < 0) { + error("Unable to create server socket"); + return; + } + + if (!hs->record_id) + hs->record_id = add_ag_record(chan); + + if (!hs->record_id) { + error("Unable to register service record"); + close(srv_sk); + return; + } + + hs->server_sk = g_io_channel_unix_new(srv_sk); + if (!hs->server_sk) { + error("Unable to allocate new GIOChannel"); + remove_ag_record(hs->record_id); + hs->record_id = 0; + close(srv_sk); + return; + } + + g_io_channel_set_close_on_unref(hs->server_sk, TRUE); + + g_io_add_watch(hs->server_sk, G_IO_IN, (GIOFunc) server_io_cb, hs); + + g_io_channel_unref(hs->server_sk); +} + +static DBusHandlerResult hs_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct headset *hs = data; + const char *interface, *member; + + assert (hs != NULL); + + interface = dbus_message_get_interface(msg); + member = dbus_message_get_member(msg); + + if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && + !strcmp("Introspect", member)) + return simple_introspect(conn, msg, data); + + if (strcmp(interface, "org.bluez.audio.Headset") != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strcmp(member, "Connect") == 0) + return hs_connect(hs, msg); + + if (strcmp(member, "Disconnect") == 0) + return hs_disconnect(hs, msg); + + if (strcmp(member, "IndicateCall") == 0) + return hs_ring(hs, msg); + + if (strcmp(member, "CancelCall") == 0) + return hs_cancel_ringing(hs, msg); + + if (strcmp(member, "Play") == 0) + return hs_play(hs, msg); + + if (strcmp(member, "Stop") == 0) + return hs_stop(hs, msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable hs_table = { + .message_function = hs_message, +}; + +struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel) +{ + static int headset_uid = 0; + struct headset *hs; + + hs = malloc(sizeof(struct headset)); + if (!hs) { + error("Allocating new hs connection struct failed!"); + return NULL; + } + + memset(hs, 0, sizeof(struct headset)); + + snprintf(hs->object_path, sizeof(hs->object_path), AUDIO_HEADSET_PATH_BASE "%d", headset_uid++); + + if (!dbus_connection_register_object_path(conn, hs->object_path, + &hs_table, hs)) { + error("D-Bus failed to register %s path", hs->object_path); + return NULL; + } + + if (channel) + hs->record_id = add_ag_record(channel); + if (bda) { + str2ba(bda, &hs->bda); + hs_connect(hs, NULL); + } + + return hs; +} + +int audio_headset_close_output(struct headset* hs) +{ + assert (hs != NULL); + + if (hs->audio_output == NULL) + return FALSE; + + g_io_channel_close(hs->audio_output); + hs->audio_output = NULL; + return FALSE; +} + +int audio_headset_open_output(struct headset* hs, const char *output) +{ + int out; + + assert (hs != NULL && output != NULL); + + audio_headset_close_output(hs); + if (output && hs->output) { + free(hs->output); + hs->output = strdup(output); } - send_simple_signal("Stopped"); + assert (hs->output); - dbus_connection_send(conn, reply, NULL); + out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT); + + if (out < 0) { + error("open(%s): %s %d", hs->output, strerror(errno), errno); + return TRUE; + } + + hs->audio_output = g_io_channel_unix_new(out); + if (!hs->audio_output) { + error("Allocating new channel for audio output!"); + return TRUE; + } + + return FALSE; +} + +int audio_headset_close_input(struct headset* hs) +{ + assert (hs != NULL); + + if (hs->audio_input == NULL) + return FALSE; + + g_io_channel_close(hs->audio_input); + hs->audio_input = NULL; + + return FALSE; +} + +int audio_headset_open_input(struct headset* hs, const char *input) +{ + int in; + + assert (hs != NULL); + + audio_headset_close_input(hs); + + /* we keep the input name, and NULL can be use to reopen */ + if (input && hs->input) { + free(hs->input); + hs->input = strdup(input); + } + + assert (hs->input); + + in = open(hs->input, O_RDONLY | O_NOCTTY); + + if (in < 0) { + error("open(%s): %s %d", hs->input, strerror(errno), errno); + return TRUE; + } + + hs->audio_input = g_io_channel_unix_new(in); + if (!hs->audio_input) { + error("Allocating new channel for audio input!"); + return TRUE; + } + return FALSE; +} + +void audio_manager_add_headset(struct manager *amanager, struct headset *hs) +{ + assert(amanager && hs); + + if (g_slist_find(amanager->headset_list, hs)) + return; + + amanager->headset_list = g_slist_append(amanager->headset_list, hs); +} + +static DBusHandlerResult am_connect(struct manager *amanager, + DBusMessage *msg) +{ + char object_path[128]; + DBusMessage *reply; + + if (!amanager) + return err_not_connected(connection, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + + snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &object_path, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult am_default_headset(struct manager *amanager, + DBusMessage *msg) +{ + DBusMessage *reply; + + if (!amanager) + return err_not_connected(connection, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult am_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *interface, *member; + struct manager *amanager = (struct manager *)data; + + interface = dbus_message_get_interface(msg); + member = dbus_message_get_member(msg); + + if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && + !strcmp("Introspect", member)) + return simple_introspect(conn, msg, data); + + if (strcmp(interface, "org.bluez.audio.Headset") != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strcmp(member, "Connect") == 0) + return am_connect(amanager, msg); + + if (strcmp(member, "DefaultHeadset") == 0) + return am_default_headset(amanager, msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable am_table = { + .message_function = am_message, +}; + +struct manager* audio_manager_new(DBusConnection *conn) +{ + struct manager* amanager; + + amanager = malloc(sizeof(struct manager)); + if (!amanager) { + error("Allocating new hs connection struct failed!"); + return NULL; + } + + memset(amanager, 0, sizeof(struct manager)); + + if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, + &am_table, amanager)) { + error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); + return NULL; + } + + return amanager; +} + +static void sig_term(int sig) +{ + g_main_loop_quit(main_loop); +} + int main(int argc, char *argv[]) { - DBusConnection *system_bus; + uint8_t opt_channel = 12; + char *opt_bda = NULL; + char *opt_input = NULL; + char *opt_output = NULL; + struct headset *hs; + struct manager *manager; struct sigaction sa; int opt; while ((opt = getopt(argc, argv, "c:o:i:")) != EOF) { switch (opt) { case 'c': - config_channel = strtol(optarg, NULL, 0); + opt_channel = strtol(optarg, NULL, 0); break; case 'i': - audio_input = optarg; + opt_input = optarg; break; case 'o': - audio_output = optarg; + opt_output = optarg; break; default: - printf("Usage: %s -c local_channel [-n] [-o output] [-i input] [bdaddr]\n", argv[0]); + printf("Usage: %s -c local_channel [-o output] [-i input] [bdaddr]\n", argv[0]); exit(1); } } - if (argv[optind]) - on_init_bda = argv[optind]; + if (optind < argc && argv[optind]) + opt_bda = argv[optind]; - start_logging("headset", "Bluetooth Headset daemon"); + start_logging("headset", "Bluetooth headset service daemon"); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; @@ -1535,27 +1771,40 @@ int main(int argc, char *argv[]) main_loop = g_main_loop_new(NULL, FALSE); - system_bus = init_dbus(NULL, NULL, NULL); - if (!system_bus) { + connection = init_dbus(NULL, NULL, NULL); + if (!connection) { error("Connection to system bus failed"); g_main_loop_unref(main_loop); exit(1); } - connection = system_bus; - - if (setup_dbus(system_bus) < 0) { - error("Connection setup failed"); - dbus_connection_unref(system_bus); + manager = audio_manager_new(connection); + if (!manager) { + error("Failed to create an audio manager"); + dbus_connection_unref(connection); g_main_loop_unref(main_loop); exit(1); } - create_server_socket(); + if (opt_bda) { + hs = audio_headset_new(connection, opt_bda, opt_channel); + if (!hs) { + error("Connection setup failed"); + dbus_connection_unref(connection); + g_main_loop_unref(main_loop); + exit(1); + } + if (opt_output) + audio_headset_open_output(hs, opt_output); + if (opt_input) + audio_headset_open_input(hs, opt_input); + audio_headset_create_server_socket(hs, opt_channel); + audio_manager_add_headset(manager, hs); + } g_main_loop_run(main_loop); - dbus_connection_unref(system_bus); + dbus_connection_unref(connection); g_main_loop_unref(main_loop); -- cgit From 76b5515202080d8a862e450092c70aaf299dbab2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Jan 2007 16:28:22 +0000 Subject: More headset.c rework --- audio/headset.c | 721 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 432 insertions(+), 289 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 78d13092..078f9332 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -67,11 +67,12 @@ struct pending_connect { }; typedef enum { - HEADSET_STATE_DISCONNECTED, - HEADSET_STATE_CONNECT_IN_PROGRESS, - HEADSET_STATE_CONNECTED, - HEADSET_STATE_PLAY_IN_PROGRESS, - HEADSET_STATE_PLAYING, + HEADSET_STATE_UNAUTHORIZED, + HEADSET_STATE_DISCONNECTED, + HEADSET_STATE_CONNECT_IN_PROGRESS, + HEADSET_STATE_CONNECTED, + HEADSET_STATE_PLAY_IN_PROGRESS, + HEADSET_STATE_PLAYING, } headset_state_t; struct headset { @@ -94,11 +95,11 @@ struct headset { headset_state_t state; struct pending_connect *connect_in_progress; - uint32_t record_id; - GIOChannel *server_sk; }; struct manager { + GIOChannel *server_sk; + uint32_t record_id; GSList *headset_list; }; @@ -107,17 +108,20 @@ static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; struct manager* audio_manager_new(DBusConnection *conn); +void audio_manager_free(struct manager* amanager); +struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda); void audio_manager_add_headset(struct manager *amanager, struct headset *hs); -static DBusHandlerResult am_default_headset(struct manager *amanager, DBusMessage *msg); -static DBusHandlerResult am_connect(struct manager *amanager, DBusMessage *msg); +void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan); +static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg); +static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg); -struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel); +struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); +void audio_headset_unref(struct headset* hs); int audio_headset_close_input(struct headset* hs); int audio_headset_open_input(struct headset* hs, const char *audio_input); int audio_headset_close_output(struct headset* hs); int audio_headset_open_output(struct headset* hs, const char *audio_output); -void audio_headset_create_server_socket(struct headset *hs, uint8_t chan); -int audio_headset_send_ring(struct headset *hs); +GIOError audio_headset_send_ring(struct headset *hs); static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg); static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); @@ -156,9 +160,9 @@ static int set_nonblocking(int fd, int *err) return 0; } -static void pending_connect_free(struct pending_connect *c, gboolean unref_io) +static void pending_connect_free(struct pending_connect *c) { - if (unref_io && c->io) + if (c->io) g_io_channel_unref(c->io); if (c->msg) dbus_message_unref(c->msg); @@ -172,7 +176,7 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, { DBusMessage *derr; - if (!conn) + if (!conn || !msg) return DBUS_HANDLER_RESULT_HANDLED; derr = dbus_message_new_error(msg, name, descr); @@ -299,10 +303,12 @@ static int parse_headset_event(const char *buf, char *rsp, int rsp_len) static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = (struct headset *)data; - int sk, ret, free_space; + struct headset *hs = data; unsigned char buf[BUF_SIZE]; char *cr; + gsize bytes_read = 0; + gsize free_space; + GIOError err; if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -312,23 +318,21 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; - sk = g_io_channel_unix_get_fd(chan); - - ret = read(sk, buf, sizeof(buf) - 1); - if (ret <= 0) + err = g_io_channel_read(chan, (gchar *)buf, sizeof(buf) - 1, &bytes_read); + if (err != G_IO_ERROR_NONE) goto failed; free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; - if (free_space < ret) { + if (free_space < bytes_read) { /* Very likely that the HS is sending us garbage so * just ignore the data and disconnect */ error("Too much data to fit incomming buffer"); goto failed; } - memcpy(&hs->buf[hs->data_start], buf, ret); - hs->data_length += ret; + memcpy(&hs->buf[hs->data_start], buf, bytes_read); + hs->data_length += bytes_read; /* Make sure the data is null terminated so we can use string * functions */ @@ -337,7 +341,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) cr = strchr(&hs->buf[hs->data_start], '\r'); if (cr) { char rsp[BUF_SIZE]; - int len, written; + gsize count, bytes_written, total_bytes_written; off_t cmd_len; cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; @@ -345,25 +349,24 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) memset(rsp, 0, sizeof(rsp)); + /* FIXME: make a better parse function */ if (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp)) == 1) hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); else hs_signal(hs, "AnswerRequested"); - len = strlen(rsp); - written = 0; + count = strlen(rsp); + total_bytes_written = bytes_written = 0; + err = G_IO_ERROR_NONE; - while (written < len) { - int ret; - - ret = write(sk, &rsp[written], len - written); - if (ret < 0) { - error("write: %s (%d)", strerror(errno), errno); - break; - } - - written += ret; - } + while (err == G_IO_ERROR_NONE && total_bytes_written < count) { + /* FIXME: make it async */ + err = g_io_channel_write(hs->rfcomm, rsp + total_bytes_written, + count - total_bytes_written, &bytes_written); + if (err != G_IO_ERROR_NONE) + error("Error while writting to the audio output channel"); + total_bytes_written += bytes_written; + }; hs->data_start += cmd_len; hs->data_length -= cmd_len; @@ -380,18 +383,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; failed: - info("Disconnected from %s", hs->object_path); - hs_signal(hs, "Disconnected"); - if (hs->sco) { - g_io_channel_close(hs->sco); - hs->sco = NULL; - } - if (hs->audio_output) { - g_io_channel_close(hs->audio_output); - hs->audio_output = NULL; - } - g_io_channel_close(chan); - hs->rfcomm = NULL; + hs_disconnect(hs, NULL); return FALSE; } @@ -402,9 +394,10 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) struct sockaddr_rc addr; socklen_t size; char hs_address[18]; - struct headset *hs = (struct headset *)data; + struct headset *hs = NULL; + struct manager *amanager = (struct manager *) data; - assert(hs != NULL); + assert(amanager != NULL); if (cond & G_IO_NVAL) { g_io_channel_unref(chan); @@ -414,7 +407,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) if (cond & (G_IO_HUP | G_IO_ERR)) { error("Hangup or error on rfcomm server socket"); g_io_channel_close(chan); - hs->server_sk = NULL; + amanager->server_sk = NULL; return TRUE; } @@ -427,7 +420,20 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (hs->state > HEADSET_STATE_DISCONNECTED) { + if ((hs = audio_manager_find_headset_by_bda(amanager, &addr.rc_bdaddr)) != NULL) { + debug("Audio manager server accept() find a matching headset in its list"); + if (!hs->object_path) { + debug("But something is wrong with the headset object path"); + return TRUE; + } + debug("Incoming connection on the server_sk for object %s", hs->object_path); + } else { + /* FIXME: make authorization dbus calls *here or in headset code? */ + hs = audio_headset_new(connection, &addr.rc_bdaddr); + /* audio_headset_authorize(hs); */ + } + + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); close(cli_sk); return TRUE; @@ -440,30 +446,36 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } + g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); + ba2str(&addr.rc_bdaddr, hs_address); debug("Accepted connection from %s, %s", hs_address, hs->object_path); hs_signal(hs, "Connected"); - g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); - return TRUE; } static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = (struct headset *)data; + struct headset *hs = data; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; GIOError err; - if (cond & G_IO_NVAL) { + if (!hs || !hs->sco) { + error("The headset is invalid or does not have a SCO connection up"); audio_headset_close_input(hs); return FALSE; } + if (cond & G_IO_NVAL) { + g_io_channel_unref(chan); + return FALSE; + } + if (cond & (G_IO_HUP | G_IO_ERR)) { audio_headset_close_input(hs); return FALSE; @@ -474,18 +486,23 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin return FALSE; total_bytes_written = bytes_written = 0; - do { + err = G_IO_ERROR_NONE; + + while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) { /* FIXME: make it async */ - err = g_io_channel_write(hs->sco, buf, bytes_read, &bytes_written); + err = g_io_channel_write(hs->sco, buf + total_bytes_written, + bytes_read - total_bytes_written, &bytes_written); + if (err != G_IO_ERROR_NONE) + error("Error while writting to the audio output channel"); total_bytes_written += bytes_written; - } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read); + }; return TRUE; } static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = (struct headset *)data; + struct headset *hs = data; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; @@ -500,10 +517,12 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond error("Audio connection got disconnected"); g_io_channel_close(chan); hs->sco = NULL; - if (hs->audio_input) { - g_io_channel_close(hs->audio_input); - hs->audio_input = NULL; + if (hs->audio_output) { + g_io_channel_close(hs->audio_output); + hs->audio_output = NULL; } + assert(hs->rfcomm); + hs->state = HEADSET_STATE_CONNECTED; hs_signal(hs, "Stopped"); return FALSE; } @@ -511,23 +530,28 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond if (!hs->audio_output && hs->output) audio_headset_open_output(hs, hs->output); + err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); + + if (err != G_IO_ERROR_NONE) + return FALSE; + if (!hs->audio_output) { error("no audio output"); - g_io_channel_close(chan); - hs->sco = NULL; return TRUE; } - err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); - if (err != G_IO_ERROR_NONE) - return FALSE; - total_bytes_written = bytes_written = 0; - do { + err = G_IO_ERROR_NONE; + + while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) { /* FIXME: make it async */ - err = g_io_channel_write(hs->audio_output, buf, bytes_read, &bytes_written); + err = g_io_channel_write(hs->audio_output, buf + total_bytes_written, + bytes_read - total_bytes_written, &bytes_written); + if (err != G_IO_ERROR_NONE) { + error("Error while writting to the audio output channel"); + } total_bytes_written += bytes_written; - } while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read); + }; return TRUE; } @@ -539,6 +563,9 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, DBusMessage *reply; socklen_t len; + assert(hs != NULL && hs->connect_in_progress != NULL && + hs->sco == NULL && hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); + if (cond & G_IO_NVAL) { g_io_channel_unref(chan); return FALSE; @@ -559,12 +586,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } - debug("SCO socket %d opened", sk); - - flags = hs->audio_output ? G_IO_IN : 0; + debug("SCO socket opened for headset %s", hs->object_path); hs->sco = chan; - g_io_add_watch(chan, flags, sco_input_to_audio_output_cb, NULL); + hs->connect_in_progress->io = NULL; + + flags = hs->audio_output ? G_IO_IN : 0; + g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); if (hs->connect_in_progress->msg) { reply = dbus_message_new_method_return(hs->connect_in_progress->msg); @@ -574,10 +602,11 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, } } + /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ if (hs->audio_input) g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs); - pending_connect_free(hs->connect_in_progress, FALSE); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; hs->state = HEADSET_STATE_PLAYING; @@ -588,10 +617,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->connect_in_progress) { err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); - pending_connect_free(hs->connect_in_progress, TRUE); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; } + assert(hs->rfcomm); + hs->state = HEADSET_STATE_CONNECTED; + return FALSE; } @@ -601,10 +633,10 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he int sk, ret, err; socklen_t len; - if (!hs->connect_in_progress) { - error("connect in progress is null in rfcomm_connect_cb!"); - return FALSE; - } + assert(hs != NULL && hs->connect_in_progress != NULL && + hs->rfcomm == NULL && + hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); + if (cond & G_IO_NVAL) { g_io_channel_unref(chan); return FALSE; @@ -643,7 +675,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } - pending_connect_free(hs->connect_in_progress, FALSE); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; } @@ -652,7 +684,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he failed: if (hs->connect_in_progress) { err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); - pending_connect_free(hs->connect_in_progress, TRUE); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; } hs->state = HEADSET_STATE_DISCONNECTED; @@ -666,7 +698,8 @@ static int rfcomm_connect(struct headset *hs, int *err) char address[18]; int sk; - assert(hs != NULL && hs->connect_in_progress != NULL); + assert(hs != NULL && hs->connect_in_progress != NULL && + hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); ba2str(&hs->bda, address); @@ -916,7 +949,7 @@ static int remove_ag_record(uint32_t rec_id) return 0; } -static void record_reply(DBusPendingCall *call, void *data) +static void get_record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; DBusError derr; @@ -927,7 +960,7 @@ static void record_reply(DBusPendingCall *call, void *data) struct headset *hs = data; struct pending_connect *c; - assert (hs != NULL && hs->connect_in_progress); + assert(hs != NULL && hs->connect_in_progress && !hs->rfcomm); c = hs->connect_in_progress; reply = dbus_pending_call_steal_reply(call); @@ -941,9 +974,14 @@ static void record_reply(DBusPendingCall *call, void *data) goto failed; } - dbus_message_get_args(reply, NULL, + if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, - DBUS_TYPE_INVALID); + DBUS_TYPE_INVALID)) { + error("Unable to get args from GetRecordReply"); + if (c->msg) + err_not_supported(c->conn, c->msg); + goto failed; + } if (!array) { error("Unable to get handle array from reply"); @@ -968,6 +1006,7 @@ static void record_reply(DBusPendingCall *call, void *data) c->ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, NULL); sdp_list_free(protos, NULL); + protos = NULL; } if (c->ch == -1) { @@ -992,12 +1031,52 @@ static void record_reply(DBusPendingCall *call, void *data) failed: if (record) sdp_record_free(record); - dbus_message_unref(reply); - pending_connect_free(hs->connect_in_progress, TRUE); + if (reply) + dbus_message_unref(reply); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; } -static void handles_reply(DBusPendingCall *call, void *data) +static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) +{ + DBusMessage *reply = NULL; + char hs_address[18]; + + if (msg) { + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + if (hs->state > HEADSET_STATE_CONNECTED) + hs_stop(hs, NULL); + + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + hs->rfcomm = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; + } + + if (hs->connect_in_progress) { + pending_connect_free(hs->connect_in_progress); + hs->connect_in_progress = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; + } + + info("Disconnected from %s, %s", hs_address, hs->object_path); + + hs_signal(hs, "Disconnected"); + + if (reply) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void get_handles_reply(DBusPendingCall *call, void *data) { DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; @@ -1009,7 +1088,7 @@ static void handles_reply(DBusPendingCall *call, void *data) dbus_uint32_t handle; int array_len; - assert (hs != NULL && hs->connect_in_progress); + assert(hs != NULL && hs->connect_in_progress); c = hs->connect_in_progress; reply = dbus_pending_call_steal_reply(call); @@ -1027,9 +1106,15 @@ static void handles_reply(DBusPendingCall *call, void *data) goto failed; } - dbus_message_get_args(reply, NULL, + if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, - DBUS_TYPE_INVALID); + DBUS_TYPE_INVALID)) { + + error("Unable to get args from reply"); + if (c->msg) + err_not_supported(c->conn, c->msg); + goto failed; + } if (!array) { error("Unable to get handle array from reply"); @@ -1073,7 +1158,7 @@ static void handles_reply(DBusPendingCall *call, void *data) goto failed; } - dbus_pending_call_set_notify(pending, record_reply, c, NULL); + dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL); dbus_message_unref(msg); dbus_message_unref(reply); @@ -1084,56 +1169,7 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - pending_connect_free(c, TRUE); -} - -static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) -{ - DBusError derr; - DBusMessage *reply; - const char *address; - char hs_address[18]; - - dbus_error_init(&derr); - - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - - if (!hs || strcasecmp(address, hs_address) != 0) - return err_not_connected(connection, msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - if (hs->sco) { - g_io_channel_close(hs->sco); - hs->sco = NULL; - } - - audio_headset_close_output(hs); - - if (hs->rfcomm) { - g_io_channel_close(hs->rfcomm); - hs->rfcomm = NULL; - } - - info("Disconnected from %s", hs_address); - - hs_signal(hs, "Disconnected"); - - dbus_connection_send(connection, reply, NULL); - - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; + hs_disconnect(hs, NULL); } static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) @@ -1142,24 +1178,11 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) const char *hs_svc = "hsp"; char hs_address[18]; - assert (hs != NULL); - - /* this block should be move to the manager Connect */ -/* if (!address && msg && conn) { */ -/* DBusError derr; */ - -/* dbus_error_init(&derr); */ - -/* dbus_message_get_args(msg, &derr, */ -/* DBUS_TYPE_STRING, &address, */ -/* DBUS_TYPE_INVALID); */ + assert(hs != NULL); -/* if (dbus_error_is_set(&derr)) { */ -/* err_invalid_args(conn, msg, derr.message); */ -/* dbus_error_free(&derr); */ -/* return DBUS_HANDLER_RESULT_HANDLED; */ -/* } */ -/* } */ + if (hs->state == HEADSET_STATE_UNAUTHORIZED) { + error("This headset has not been audiothorized"); + } if (hs->state > HEADSET_STATE_DISCONNECTED || hs->connect_in_progress) { error("Already connected"); @@ -1170,7 +1193,8 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) if (!hs->connect_in_progress) { error("Out of memory when allocating new struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; - } + } + hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); hs->connect_in_progress->conn = dbus_connection_ref(connection); @@ -1180,8 +1204,10 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { - pending_connect_free(hs->connect_in_progress, TRUE); + error("Could not create a new dbus message"); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -1190,51 +1216,48 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); - pending_connect_free(hs->connect_in_progress, TRUE); + pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; dbus_message_unref(msg); return err_connect_failed(connection, msg, EIO); } - dbus_pending_call_set_notify(pending, handles_reply, hs->connect_in_progress, NULL); + dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); dbus_message_unref(msg); return DBUS_HANDLER_RESULT_HANDLED;; } -int audio_headset_send_ring(struct headset *hs) +GIOError audio_headset_send_ring(struct headset *hs) { const char *ring_str = "\r\nRING\r\n"; - int sk, written, len; + GIOError err; + gsize total_written, written, count; - assert (hs != NULL); - if (!hs->rfcomm) { + assert(hs != NULL); + if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { error("the headset %s is not connected", hs->object_path); + return G_IO_ERROR_UNKNOWN; } - sk = g_io_channel_unix_get_fd(hs->rfcomm); - - len = strlen(ring_str); - written = 0; - - while (written < len) { - int ret; - - ret = write(sk, ring_str + written, len - written); - - if (ret < 0) - return ret; + count = strlen(ring_str); + written = total_written = 0; - written += ret; + while (total_written < count) { + err = g_io_channel_write(hs->rfcomm, ring_str + total_written, + count - total_written, &written); + if (err != G_IO_ERROR_NONE) + return err; + total_written += written; } - return 0; + return G_IO_ERROR_NONE; } -static gboolean ring_timer(gpointer data) +static gboolean ring_timer_cb(gpointer data) { struct headset *hs = data; @@ -1248,16 +1271,18 @@ static gboolean ring_timer(gpointer data) static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) { - DBusMessage *reply; + DBusMessage *reply = NULL; assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(connection, msg); - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + if (msg) { + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } if (hs->ring_timer) { debug("Got Ring method call while ringing already in progress"); @@ -1269,25 +1294,29 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) return err_failed(connection, msg); } - hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer, hs); + hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); done: - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); + if (reply) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) { - DBusMessage *reply; + DBusMessage *reply = NULL; if (!hs) return err_not_connected(connection, msg); - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + if (msg) { + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } if (!hs->ring_timer) { debug("Got CancelRinging method call but ringing is not in progress"); @@ -1298,8 +1327,10 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) hs->ring_timer = 0; done: - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); + if (reply) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } return DBUS_HANDLER_RESULT_HANDLED; } @@ -1314,7 +1345,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (!hs) return err_not_connected(connection, msg); - if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) + if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) return err_already_connected(connection, msg); /* FIXME: in progress error? */ if (hs->sco) @@ -1324,11 +1355,12 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (!hs->connect_in_progress) return DBUS_HANDLER_RESULT_NEED_MEMORY; + hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); c = hs->connect_in_progress; c->conn = dbus_connection_ref(connection); - c->msg = dbus_message_ref(msg); + c->msg = msg ? dbus_message_ref(msg) : NULL; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { @@ -1341,7 +1373,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) c->io = g_io_channel_unix_new(sk); if (!c->io) { close(sk); - pending_connect_free(c, TRUE); + pending_connect_free(c); return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -1350,6 +1382,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, BDADDR_ANY); + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); @@ -1375,7 +1408,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) debug("Connect in progress"); - g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, c); + g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, hs); } else { debug("Connect succeeded with first try"); sco_connect_cb(c->io, G_IO_OUT, hs); @@ -1384,75 +1417,47 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) return 0; failed: - if (c) - pending_connect_free(c, TRUE); - close(sk); + if (hs->connect_in_progress) { + pending_connect_free(hs->connect_in_progress); + hs->connect_in_progress = NULL; + } return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) { - DBusMessage *reply; + DBusMessage *reply = NULL; if (!hs || !hs->sco) return err_not_connected(connection, msg); - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - g_io_channel_close(hs->sco); - hs->sco = NULL; - - hs_signal(hs, "Stopped"); - hs->state = HEADSET_STATE_CONNECTED; - - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; -} - -void audio_headset_create_server_socket(struct headset *hs, uint8_t chan) -{ - int srv_sk; - - assert (hs != NULL); - - if (hs->server_sk) { - error("Server socket already created"); - return; - } - - srv_sk = server_socket(&chan); - if (srv_sk < 0) { - error("Unable to create server socket"); - return; + if (msg) { + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; } - if (!hs->record_id) - hs->record_id = add_ag_record(chan); - - if (!hs->record_id) { - error("Unable to register service record"); - close(srv_sk); - return; + if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->connect_in_progress) { + pending_connect_free(hs->connect_in_progress); + hs->connect_in_progress = NULL; + hs->state = HEADSET_STATE_CONNECTED; } - hs->server_sk = g_io_channel_unix_new(srv_sk); - if (!hs->server_sk) { - error("Unable to allocate new GIOChannel"); - remove_ag_record(hs->record_id); - hs->record_id = 0; - close(srv_sk); - return; + if (hs->sco) { + g_io_channel_close(hs->sco); + hs->sco = NULL; + hs->state = HEADSET_STATE_CONNECTED; } - g_io_channel_set_close_on_unref(hs->server_sk, TRUE); + hs_signal(hs, "Stopped"); + hs->state = HEADSET_STATE_CONNECTED; - g_io_add_watch(hs->server_sk, G_IO_IN, (GIOFunc) server_io_cb, hs); + if (reply) { + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); + } - g_io_channel_unref(hs->server_sk); + return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult hs_message(DBusConnection *conn, @@ -1461,7 +1466,7 @@ static DBusHandlerResult hs_message(DBusConnection *conn, struct headset *hs = data; const char *interface, *member; - assert (hs != NULL); + assert(hs != NULL); interface = dbus_message_get_interface(msg); member = dbus_message_get_member(msg); @@ -1498,7 +1503,11 @@ static const DBusObjectPathVTable hs_table = { .message_function = hs_message, }; -struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int channel) +/* +** audio_headset_new: +** Create a unique dbus object path for the headset and allocates a new headset or return NULL if fail +*/ +struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) { static int headset_uid = 0; struct headset *hs; @@ -1516,44 +1525,48 @@ struct headset* audio_headset_new(DBusConnection *conn, const char *bda, int cha if (!dbus_connection_register_object_path(conn, hs->object_path, &hs_table, hs)) { error("D-Bus failed to register %s path", hs->object_path); + free (hs); return NULL; } - if (channel) - hs->record_id = add_ag_record(channel); - if (bda) { - str2ba(bda, &hs->bda); - hs_connect(hs, NULL); - } + bacpy(&hs->bda, bda); return hs; } +void audio_headset_unref(struct headset* hs) +{ + assert(hs != NULL); + + free(hs); +} + int audio_headset_close_output(struct headset* hs) { - assert (hs != NULL); + assert(hs != NULL); if (hs->audio_output == NULL) return FALSE; - g_io_channel_close(hs->audio_output); + g_io_channel_unref(hs->audio_output); hs->audio_output = NULL; return FALSE; } +/* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ int audio_headset_open_output(struct headset* hs, const char *output) { int out; - assert (hs != NULL && output != NULL); - + assert(hs != NULL && output != NULL); + audio_headset_close_output(hs); if (output && hs->output) { free(hs->output); hs->output = strdup(output); } - assert (hs->output); + assert(hs->output); out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT); @@ -1568,17 +1581,19 @@ int audio_headset_open_output(struct headset* hs, const char *output) return TRUE; } + g_io_channel_set_close_on_unref(hs->audio_output, TRUE); + return FALSE; } int audio_headset_close_input(struct headset* hs) { - assert (hs != NULL); + assert(hs != NULL); if (hs->audio_input == NULL) return FALSE; - g_io_channel_close(hs->audio_input); + g_io_channel_unref(hs->audio_input); hs->audio_input = NULL; return FALSE; @@ -1588,8 +1603,8 @@ int audio_headset_open_input(struct headset* hs, const char *input) { int in; - assert (hs != NULL); - + assert(hs != NULL); + audio_headset_close_input(hs); /* we keep the input name, and NULL can be use to reopen */ @@ -1598,7 +1613,7 @@ int audio_headset_open_input(struct headset* hs, const char *input) hs->input = strdup(input); } - assert (hs->input); + assert(hs->input); in = open(hs->input, O_RDONLY | O_NOCTTY); @@ -1612,9 +1627,70 @@ int audio_headset_open_input(struct headset* hs, const char *input) error("Allocating new channel for audio input!"); return TRUE; } + g_io_channel_set_close_on_unref(hs->audio_input, TRUE); + return FALSE; } +void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) +{ + int srv_sk; + + assert(amanager != NULL); + + if (amanager->server_sk) { + error("Server socket already created"); + return; + } + + srv_sk = server_socket(&chan); + if (srv_sk < 0) { + error("Unable to create server socket"); + return; + } + + if (!amanager->record_id) + amanager->record_id = add_ag_record(chan); + + if (!amanager->record_id) { + error("Unable to register service record"); + close(srv_sk); + return; + } + + amanager->server_sk = g_io_channel_unix_new(srv_sk); + if (!amanager->server_sk) { + error("Unable to allocate new GIOChannel"); + remove_ag_record(amanager->record_id); + amanager->record_id = 0; + close(srv_sk); + return; + } + + g_io_channel_set_close_on_unref(amanager->server_sk, TRUE); + + g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); + + g_io_channel_unref(amanager->server_sk); +} + +static gint headset_bda_cmp(gconstpointer aheadset, gconstpointer bda) +{ + const struct headset *hs = aheadset; + + return bacmp(&hs->bda, bda); +} + +struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda) +{ + GSList *elem; + + assert(amanager); + elem = g_slist_find_custom(amanager->headset_list, bda, headset_bda_cmp); + + return elem ? elem->data : NULL; +} + void audio_manager_add_headset(struct manager *amanager, struct headset *hs) { assert(amanager && hs); @@ -1625,22 +1701,42 @@ void audio_manager_add_headset(struct manager *amanager, struct headset *hs) amanager->headset_list = g_slist_append(amanager->headset_list, hs); } -static DBusHandlerResult am_connect(struct manager *amanager, - DBusMessage *msg) +static DBusHandlerResult am_create_headset(struct manager *amanager, + DBusMessage *msg) { - char object_path[128]; + const char *address; + struct headset *hs; + bdaddr_t bda; DBusMessage *reply; + DBusError derr; if (!amanager) return err_not_connected(connection, msg); + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID)) { + err_invalid_args(connection, msg, derr.message); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_error_is_set(&derr)) { + err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - - snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0); - dbus_message_append_args(msg, DBUS_TYPE_STRING, &object_path, + str2ba(address, &bda); + if (!(hs = audio_manager_find_headset_by_bda(amanager, &bda))) { + hs = audio_headset_new(connection, &bda); + } + /* FIXME: we could send an error if the headset was already created or silently fail */ + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs->object_path, DBUS_TYPE_INVALID); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); @@ -1648,10 +1744,11 @@ static DBusHandlerResult am_connect(struct manager *amanager, return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult am_default_headset(struct manager *amanager, +static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg) { DBusMessage *reply; + char object_path[128]; if (!amanager) return err_not_connected(connection, msg); @@ -1660,6 +1757,10 @@ static DBusHandlerResult am_default_headset(struct manager *amanager, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; + snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, + DBUS_TYPE_INVALID); + dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); @@ -1682,11 +1783,11 @@ static DBusHandlerResult am_message(DBusConnection *conn, if (strcmp(interface, "org.bluez.audio.Headset") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (strcmp(member, "Connect") == 0) - return am_connect(amanager, msg); + if (strcmp(member, "CreateHeadset") == 0) + return am_create_headset(amanager, msg); if (strcmp(member, "DefaultHeadset") == 0) - return am_default_headset(amanager, msg); + return am_get_default_headset(amanager, msg); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1697,9 +1798,10 @@ static const DBusObjectPathVTable am_table = { struct manager* audio_manager_new(DBusConnection *conn) { - struct manager* amanager; + struct manager *amanager; amanager = malloc(sizeof(struct manager)); + if (!amanager) { error("Allocating new hs connection struct failed!"); return NULL; @@ -1710,12 +1812,43 @@ struct manager* audio_manager_new(DBusConnection *conn) if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, &am_table, amanager)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); + free(amanager); return NULL; } return amanager; } +static void headset_list_unref_each(gpointer aheadset, gpointer am) +{ + struct headset *hs = aheadset; + + audio_headset_unref(hs); +} + +void audio_manager_free(struct manager* amanager) +{ + assert(amanager != NULL); + + if (amanager->record_id) { + remove_ag_record(amanager->record_id); + amanager->record_id = 0; + } + + if (amanager->server_sk) { + g_io_channel_unref(amanager->server_sk); + amanager->server_sk = NULL; + } + + if (amanager->headset_list) { + g_slist_foreach(amanager->headset_list, headset_list_unref_each, amanager); + g_slist_free(amanager->headset_list); + amanager->headset_list = NULL; + } + + free(amanager); +} + static void sig_term(int sig) { g_main_loop_quit(main_loop); @@ -1727,6 +1860,7 @@ int main(int argc, char *argv[]) char *opt_bda = NULL; char *opt_input = NULL; char *opt_output = NULL; + bdaddr_t bda; struct headset *hs; struct manager *manager; struct sigaction sa; @@ -1786,24 +1920,33 @@ int main(int argc, char *argv[]) exit(1); } + audio_manager_create_headset_server(manager, opt_channel); + if (opt_bda) { - hs = audio_headset_new(connection, opt_bda, opt_channel); + str2ba(opt_bda, &bda); + hs = audio_headset_new(connection, &bda); if (!hs) { error("Connection setup failed"); dbus_connection_unref(connection); g_main_loop_unref(main_loop); exit(1); } + if (opt_output) audio_headset_open_output(hs, opt_output); if (opt_input) audio_headset_open_input(hs, opt_input); - audio_headset_create_server_socket(hs, opt_channel); + audio_manager_add_headset(manager, hs); + /* connect */ + hs_connect(hs, NULL); } g_main_loop_run(main_loop); + audio_manager_free(manager); + manager = NULL; + dbus_connection_unref(connection); g_main_loop_unref(main_loop); -- cgit From 79e768bf14609f8f5a91a249687beac14b68687e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Jan 2007 19:34:16 +0000 Subject: headsetd fixes --- audio/headset.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 078f9332..1e7a6aad 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1043,6 +1043,8 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) DBusMessage *reply = NULL; char hs_address[18]; + assert(hs); + if (msg) { reply = dbus_message_new_method_return(msg); if (!reply) @@ -1064,7 +1066,8 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) hs->state = HEADSET_STATE_DISCONNECTED; } - info("Disconnected from %s, %s", hs_address, hs->object_path); + ba2str(&hs->bda, hs_address); + info("Disconnected from %s, %s", &hs_address, hs->object_path ? hs->object_path : "null"); hs_signal(hs, "Disconnected"); @@ -1176,6 +1179,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) { DBusPendingCall *pending; const char *hs_svc = "hsp"; + const char *addr_ptr; char hs_address[18]; assert(hs != NULL); @@ -1212,7 +1216,8 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) } ba2str(&hs->bda, hs_address); - dbus_message_append_args(msg, DBUS_TYPE_STRING, &hs_address, + addr_ptr = hs_address; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); @@ -1670,8 +1675,6 @@ void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) g_io_channel_set_close_on_unref(amanager->server_sk, TRUE); g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); - - g_io_channel_unref(amanager->server_sk); } static gint headset_bda_cmp(gconstpointer aheadset, gconstpointer bda) -- cgit From a0aeaf15caa9e967f6be1a7b3a3087ff9122c7a9 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Wed, 24 Jan 2007 19:47:56 +0000 Subject: Moved set_nonblocking to common/dbus.c --- audio/headset.c | 28 ---------------------------- 1 file changed, 28 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1e7a6aad..55a47e8e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -132,34 +132,6 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg); static void hs_signal(struct headset *hs, const char *name); static void hs_signal_gain_setting(struct headset *hs, const char *buf); -static int set_nonblocking(int fd, int *err) -{ - long arg; - - arg = fcntl(fd, F_GETFL); - if (arg < 0) { - if (err) - *err = errno; - error("fcntl(F_GETFL): %s (%d)", strerror(errno), errno); - return -1; - } - - /* Return if already nonblocking */ - if (arg & O_NONBLOCK) - return 0; - - arg |= O_NONBLOCK; - if (fcntl(fd, F_SETFL, arg) < 0) { - if (err) - *err = errno; - error("fcntl(F_SETFL, O_NONBLOCK): %s (%d)", - strerror(errno), errno); - return -1; - } - - return 0; -} - static void pending_connect_free(struct pending_connect *c) { if (c->io) -- cgit From 97e3051ec51ac5bdcb37a371249360163eaf5709 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Thu, 25 Jan 2007 17:27:52 +0000 Subject: set_nonblocking: removed err argument --- audio/headset.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 55a47e8e..cb33f149 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -697,8 +697,10 @@ static int rfcomm_connect(struct headset *hs, int *err) goto failed; } - if (set_nonblocking(sk, err) < 0) + if (set_nonblocking(sk) < 0) { + *err = errno; goto failed; + } memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; @@ -1367,7 +1369,8 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) goto failed; } - if (set_nonblocking(sk, &err) < 0) { + if (set_nonblocking(sk) < 0) { + err = errno; err_connect_failed(connection, msg, err); goto failed; } -- cgit From 61f64a95d1e0e8cf4f79b799394444d4bc03b5f7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 31 Jan 2007 08:52:52 +0000 Subject: Add skeleton for embedding SBC library --- audio/Makefile.am | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index f7e48a3f..5fbbcd21 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -6,12 +6,15 @@ noinst_PROGRAMS = bluetoothd-service-audio \ bluetoothd_service_audio_SOURCES = main.c +bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ + @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ + bluetoothd_service_headset_SOURCES = headset.c -LDADD = $(top_builddir)/common/libhelper.a \ - @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +bluetoothd_service_headset_LDADD = $(top_builddir)/common/libhelper.a \ + @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @SBC_CFLAGS@ INCLUDES = -I$(top_srcdir)/common -- cgit From a9dc7333cfb0d248d7c8486362bfdfd3a8fb32e5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 11 Feb 2007 18:15:18 +0000 Subject: Further headsetd fixes --- audio/headset.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index cb33f149..81f24f99 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1318,14 +1318,16 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) { struct sockaddr_sco addr; struct pending_connect *c; - char hs_address[18]; int sk, err; if (!hs) return err_not_connected(connection, msg); - if (hs->state <= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) - return err_already_connected(connection, msg); /* FIXME: in progress error? */ + if (hs->state < HEADSET_STATE_CONNECTED) + return err_not_connected(connection, msg); /* FIXME: in progress error? */ + + if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) + return err_already_connected(connection, msg); if (hs->sco) return err_already_connected(connection, msg); @@ -1377,7 +1379,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - str2ba(hs_address, &addr.sco_bdaddr); + bacpy(&addr.sco_bdaddr, &hs->bda); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -1652,9 +1654,9 @@ void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); } -static gint headset_bda_cmp(gconstpointer aheadset, gconstpointer bda) +static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) { - const struct headset *hs = aheadset; + const struct headset *hs = headset; return bacmp(&hs->bda, bda); } @@ -1682,6 +1684,7 @@ void audio_manager_add_headset(struct manager *amanager, struct headset *hs) static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg) { + const char *object_path; const char *address; struct headset *hs; bdaddr_t bda; @@ -1709,12 +1712,13 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, return DBUS_HANDLER_RESULT_NEED_MEMORY; str2ba(address, &bda); - if (!(hs = audio_manager_find_headset_by_bda(amanager, &bda))) { + hs = audio_manager_find_headset_by_bda(amanager, &bda); + if (!hs) hs = audio_headset_new(connection, &bda); - } /* FIXME: we could send an error if the headset was already created or silently fail */ - dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs->object_path, + object_path = hs->object_path; + dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, DBUS_TYPE_INVALID); dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); @@ -1727,6 +1731,7 @@ static DBusHandlerResult am_get_default_headset(struct manager *amanager, { DBusMessage *reply; char object_path[128]; + const char *opath = object_path; if (!amanager) return err_not_connected(connection, msg); @@ -1736,7 +1741,7 @@ static DBusHandlerResult am_get_default_headset(struct manager *amanager, return DBUS_HANDLER_RESULT_NEED_MEMORY; snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); dbus_connection_send(connection, reply, NULL); @@ -1844,7 +1849,7 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt; - while ((opt = getopt(argc, argv, "c:o:i:")) != EOF) { + while ((opt = getopt(argc, argv, "c:o:i:d")) != EOF) { switch (opt) { case 'c': opt_channel = strtol(optarg, NULL, 0); @@ -1858,8 +1863,12 @@ int main(int argc, char *argv[]) opt_output = optarg; break; + case 'd': + enable_debug(); + break; + default: - printf("Usage: %s -c local_channel [-o output] [-i input] [bdaddr]\n", argv[0]); + printf("Usage: %s -c local_channel [-d] [-o output] [-i input] [bdaddr]\n", argv[0]); exit(1); } } @@ -1879,11 +1888,9 @@ int main(int argc, char *argv[]) sigaction(SIGCHLD, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); - enable_debug(); - main_loop = g_main_loop_new(NULL, FALSE); - connection = init_dbus(NULL, NULL, NULL); + connection = init_dbus("org.bluez.audio", NULL, NULL); if (!connection) { error("Connection to system bus failed"); g_main_loop_unref(main_loop); -- cgit From ca470c662b2b43b6a65762a7f721cb1c0ca17478 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 11 Feb 2007 22:01:18 +0000 Subject: Update device state when accepting an incomming connection --- audio/headset.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 81f24f99..0591a8f8 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -424,6 +424,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) debug("Accepted connection from %s, %s", hs_address, hs->object_path); + hs->state = HEADSET_STATE_CONNECTED; hs_signal(hs, "Connected"); return TRUE; -- cgit From 2b62b5d7ee5dfdf0101fe14f4b0b78560d179cb5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 14 Feb 2007 22:31:08 +0000 Subject: Cleanup --- audio/headset.c | 87 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0591a8f8..cace9ff8 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -111,16 +111,16 @@ struct manager* audio_manager_new(DBusConnection *conn); void audio_manager_free(struct manager* amanager); struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda); void audio_manager_add_headset(struct manager *amanager, struct headset *hs); -void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan); +gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t chan); static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg); static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg); struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); void audio_headset_unref(struct headset* hs); -int audio_headset_close_input(struct headset* hs); -int audio_headset_open_input(struct headset* hs, const char *audio_input); -int audio_headset_close_output(struct headset* hs); -int audio_headset_open_output(struct headset* hs, const char *audio_output); +gboolean audio_headset_close_input(struct headset* hs); +gboolean audio_headset_open_input(struct headset* hs, const char *audio_input); +gboolean audio_headset_close_output(struct headset* hs); +gboolean audio_headset_open_output(struct headset* hs, const char *audio_output); GIOError audio_headset_send_ring(struct headset *hs); static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg); @@ -455,8 +455,10 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin } err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); - if (err != G_IO_ERROR_NONE) + if (err != G_IO_ERROR_NONE) { + audio_headset_close_input(hs); return FALSE; + } total_bytes_written = bytes_written = 0; err = G_IO_ERROR_NONE; @@ -660,6 +662,7 @@ failed: pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; } + hs->state = HEADSET_STATE_DISCONNECTED; return FALSE; @@ -979,7 +982,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (!sdp_get_access_protos(record, &protos)) { c->ch = sdp_get_proto_port(protos, RFCOMM_UUID); - sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, NULL); + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); protos = NULL; } @@ -1168,14 +1171,14 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } - hs->connect_in_progress = malloc(sizeof(struct pending_connect)); + hs->connect_in_progress = g_try_new0(struct pending_connect, 1); if (!hs->connect_in_progress) { error("Out of memory when allocating new struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } + hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; - memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); hs->connect_in_progress->conn = dbus_connection_ref(connection); hs->connect_in_progress->msg = msg ? dbus_message_ref(msg) : NULL; @@ -1488,22 +1491,22 @@ static const DBusObjectPathVTable hs_table = { /* ** audio_headset_new: -** Create a unique dbus object path for the headset and allocates a new headset or return NULL if fail +** Create a unique dbus object path for the headset and allocates a new +** headset or return NULL if fail */ -struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) +struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) { static int headset_uid = 0; struct headset *hs; - hs = malloc(sizeof(struct headset)); + hs = g_try_new0(struct headset, 1); if (!hs) { error("Allocating new hs connection struct failed!"); return NULL; } - memset(hs, 0, sizeof(struct headset)); - - snprintf(hs->object_path, sizeof(hs->object_path), AUDIO_HEADSET_PATH_BASE "%d", headset_uid++); + snprintf(hs->object_path, sizeof(hs->object_path), + AUDIO_HEADSET_PATH_BASE "%d", headset_uid++); if (!dbus_connection_register_object_path(conn, hs->object_path, &hs_table, hs)) { @@ -1517,14 +1520,14 @@ struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) return hs; } -void audio_headset_unref(struct headset* hs) +void audio_headset_unref(struct headset *hs) { assert(hs != NULL); free(hs); } -int audio_headset_close_output(struct headset* hs) +gboolean audio_headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -1533,11 +1536,12 @@ int audio_headset_close_output(struct headset* hs) g_io_channel_unref(hs->audio_output); hs->audio_output = NULL; - return FALSE; + + return TRUE; } /* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ -int audio_headset_open_output(struct headset* hs, const char *output) +gboolean audio_headset_open_output(struct headset *hs, const char *output) { int out; @@ -1555,21 +1559,21 @@ int audio_headset_open_output(struct headset* hs, const char *output) if (out < 0) { error("open(%s): %s %d", hs->output, strerror(errno), errno); - return TRUE; + return FALSE; } hs->audio_output = g_io_channel_unix_new(out); if (!hs->audio_output) { error("Allocating new channel for audio output!"); - return TRUE; + return FALSE; } g_io_channel_set_close_on_unref(hs->audio_output, TRUE); - return FALSE; + return TRUE; } -int audio_headset_close_input(struct headset* hs) +gboolean audio_headset_close_input(struct headset *hs) { assert(hs != NULL); @@ -1579,10 +1583,12 @@ int audio_headset_close_input(struct headset* hs) g_io_channel_unref(hs->audio_input); hs->audio_input = NULL; - return FALSE; + hs->state = HEADSET_STATE_CONNECTED; + + return TRUE; } -int audio_headset_open_input(struct headset* hs, const char *input) +gboolean audio_headset_open_input(struct headset *hs, const char *input) { int in; @@ -1602,20 +1608,21 @@ int audio_headset_open_input(struct headset* hs, const char *input) if (in < 0) { error("open(%s): %s %d", hs->input, strerror(errno), errno); - return TRUE; + return FALSE; } hs->audio_input = g_io_channel_unix_new(in); if (!hs->audio_input) { error("Allocating new channel for audio input!"); - return TRUE; + return FALSE; } + g_io_channel_set_close_on_unref(hs->audio_input, TRUE); - return FALSE; + return TRUE; } -void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) +gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) { int srv_sk; @@ -1623,13 +1630,13 @@ void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) if (amanager->server_sk) { error("Server socket already created"); - return; + return FALSE; } srv_sk = server_socket(&chan); if (srv_sk < 0) { error("Unable to create server socket"); - return; + return FALSE; } if (!amanager->record_id) @@ -1638,7 +1645,7 @@ void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) if (!amanager->record_id) { error("Unable to register service record"); close(srv_sk); - return; + return FALSE; } amanager->server_sk = g_io_channel_unix_new(srv_sk); @@ -1647,12 +1654,14 @@ void audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) remove_ag_record(amanager->record_id); amanager->record_id = 0; close(srv_sk); - return; + return FALSE; } g_io_channel_set_close_on_unref(amanager->server_sk, TRUE); g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); + + return TRUE; } static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) @@ -1662,7 +1671,7 @@ static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) return bacmp(&hs->bda, bda); } -struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda) +struct headset *audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda) { GSList *elem; @@ -1803,13 +1812,6 @@ struct manager* audio_manager_new(DBusConnection *conn) return amanager; } -static void headset_list_unref_each(gpointer aheadset, gpointer am) -{ - struct headset *hs = aheadset; - - audio_headset_unref(hs); -} - void audio_manager_free(struct manager* amanager) { assert(amanager != NULL); @@ -1825,7 +1827,8 @@ void audio_manager_free(struct manager* amanager) } if (amanager->headset_list) { - g_slist_foreach(amanager->headset_list, headset_list_unref_each, amanager); + g_slist_foreach(amanager->headset_list, (GFunc) audio_headset_unref, + amanager); g_slist_free(amanager->headset_list); amanager->headset_list = NULL; } -- cgit From 68960ec8215ace07f671a997779d99b1cf9f0605 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 01:33:52 +0000 Subject: Further fixes and cleanup --- audio/headset.c | 111 ++++++++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 63 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index cace9ff8..32db5444 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -107,20 +107,20 @@ struct manager { static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; -struct manager* audio_manager_new(DBusConnection *conn); -void audio_manager_free(struct manager* amanager); -struct headset* audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda); +struct manager *audio_manager_new(DBusConnection *conn); +void audio_manager_free(struct manager *amanager); +struct headset *audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda); void audio_manager_add_headset(struct manager *amanager, struct headset *hs); gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t chan); static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg); static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg); -struct headset* audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); -void audio_headset_unref(struct headset* hs); -gboolean audio_headset_close_input(struct headset* hs); -gboolean audio_headset_open_input(struct headset* hs, const char *audio_input); -gboolean audio_headset_close_output(struct headset* hs); -gboolean audio_headset_open_output(struct headset* hs, const char *audio_output); +struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); +void audio_headset_unref(struct headset *hs); +gboolean audio_headset_close_input(struct headset *hs); +gboolean audio_headset_open_input(struct headset *hs, const char *audio_input); +gboolean audio_headset_close_output(struct headset *hs); +gboolean audio_headset_open_output(struct headset *hs, const char *audio_output); GIOError audio_headset_send_ring(struct headset *hs); static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg); @@ -152,13 +152,12 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED; derr = dbus_message_new_error(msg, name, descr); - if (derr) { - dbus_connection_send(conn, derr, NULL); - return DBUS_HANDLER_RESULT_HANDLED; - } else { + if (!derr) { error("Unable to allocate new error return"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } + + return send_message_and_unref(conn, derr); } static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, @@ -230,8 +229,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); + send_message_and_unref(connection, signal); } static void hs_signal(struct headset *hs, const char *name) @@ -244,8 +242,7 @@ static void hs_signal(struct headset *hs, const char *name) return; } - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); + send_message_and_unref(connection, signal); } static int parse_headset_event(const char *buf, char *rsp, int rsp_len) @@ -392,19 +389,20 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if ((hs = audio_manager_find_headset_by_bda(amanager, &addr.rc_bdaddr)) != NULL) { - debug("Audio manager server accept() find a matching headset in its list"); - if (!hs->object_path) { - debug("But something is wrong with the headset object path"); + hs = audio_manager_find_headset_by_bda(amanager, &addr.rc_bdaddr); + if (!hs) { + hs = audio_headset_new(connection, &addr.rc_bdaddr); + if (!hs) { + error("Unable to create a new headset object"); + close(cli_sk); return TRUE; } - debug("Incoming connection on the server_sk for object %s", hs->object_path); - } else { - /* FIXME: make authorization dbus calls *here or in headset code? */ - hs = audio_headset_new(connection, &addr.rc_bdaddr); - /* audio_headset_authorize(hs); */ } + /* audio_headset_authorize(hs); */ + + debug("Incoming connection on the server_sk for object %s", hs->object_path); + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); close(cli_sk); @@ -571,10 +569,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, if (hs->connect_in_progress->msg) { reply = dbus_message_new_method_return(hs->connect_in_progress->msg); - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); } /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ @@ -646,10 +642,8 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he DBusMessage *reply; reply = dbus_message_new_method_return(hs->connect_in_progress->msg); - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; } @@ -1035,24 +1029,22 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); hs->rfcomm = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; } if (hs->connect_in_progress) { pending_connect_free(hs->connect_in_progress); hs->connect_in_progress = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; } + hs->state = HEADSET_STATE_DISCONNECTED; + ba2str(&hs->bda, hs_address); info("Disconnected from %s, %s", &hs_address, hs->object_path ? hs->object_path : "null"); hs_signal(hs, "Disconnected"); - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1246,7 +1238,7 @@ static gboolean ring_timer_cb(gpointer data) assert(hs != NULL); - if (audio_headset_send_ring(hs) < 0) + if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) error("Sending RING failed"); return TRUE; @@ -1272,7 +1264,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) goto done; } - if (audio_headset_send_ring(hs) < 0) { + if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) { dbus_message_unref(reply); return err_failed(connection, msg); } @@ -1280,10 +1272,8 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); done: - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1310,10 +1300,8 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) hs->ring_timer = 0; done: - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1438,10 +1426,8 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) hs_signal(hs, "Stopped"); hs->state = HEADSET_STATE_CONNECTED; - if (reply) { - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - } + if (reply) + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1723,17 +1709,19 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, str2ba(address, &bda); hs = audio_manager_find_headset_by_bda(amanager, &bda); - if (!hs) + if (!hs) { hs = audio_headset_new(connection, &bda); - /* FIXME: we could send an error if the headset was already created or silently fail */ + if (!hs) + return error_reply(connection, msg, + "org.bluez.Error.Failed", + "Unable to create new headset object"); + } object_path = hs->object_path; dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, DBUS_TYPE_INVALID); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; + return send_message_and_unref(connection, reply); } static DBusHandlerResult am_get_default_headset(struct manager *amanager, @@ -1754,10 +1742,7 @@ static DBusHandlerResult am_get_default_headset(struct manager *amanager, dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); - dbus_connection_send(connection, reply, NULL); - dbus_message_unref(reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return send_message_and_unref(connection, reply); } static DBusHandlerResult am_message(DBusConnection *conn, -- cgit From c9395045adf8b1a3c4ed7407ec7f492417bc28f0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 07:20:44 +0000 Subject: Allow registering as an external service (for debugin purposes) --- audio/headset.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 32db5444..82909701 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1821,6 +1821,38 @@ void audio_manager_free(struct manager* amanager) free(amanager); } +static gboolean register_service(const char *ident, const char *name, + const char *desc) +{ + DBusMessage *msg, *reply; + DBusError derr; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", "RegisterService"); + + if (!msg) { + error("Unable to allocate new message"); + return FALSE; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &ident, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &desc, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); + if (dbus_error_is_set(&derr)) { + error("RegisterService: %s", derr.message); + dbus_error_free(&derr); + return FALSE; + } + + dbus_message_unref(reply); + + return TRUE; +} + static void sig_term(int sig) { g_main_loop_quit(main_loop); @@ -1832,13 +1864,14 @@ int main(int argc, char *argv[]) char *opt_bda = NULL; char *opt_input = NULL; char *opt_output = NULL; + gboolean register_svc = FALSE; bdaddr_t bda; struct headset *hs; struct manager *manager; struct sigaction sa; int opt; - while ((opt = getopt(argc, argv, "c:o:i:d")) != EOF) { + while ((opt = getopt(argc, argv, "c:o:i:dr")) != EOF) { switch (opt) { case 'c': opt_channel = strtol(optarg, NULL, 0); @@ -1856,6 +1889,10 @@ int main(int argc, char *argv[]) enable_debug(); break; + case 'r': + register_svc = TRUE; + break; + default: printf("Usage: %s -c local_channel [-d] [-o output] [-i input] [bdaddr]\n", argv[0]); exit(1); @@ -1886,6 +1923,14 @@ int main(int argc, char *argv[]) exit(1); } + if (register_svc && !register_service("headset", "Headset service", + "Headset service")) { + error("Unable to register service"); + dbus_connection_unref(connection); + g_main_loop_unref(main_loop); + exit(1); + } + manager = audio_manager_new(connection); if (!manager) { error("Failed to create an audio manager"); -- cgit From 7d163e45e7a5a22f7ac52982587704f6a3370e05 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 08:35:16 +0000 Subject: More cleanup and fixes --- audio/headset.c | 172 ++++++++++++++++++++++++++------------------------------ 1 file changed, 81 insertions(+), 91 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 82909701..79c85084 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -94,7 +94,7 @@ struct headset { int data_length; headset_state_t state; - struct pending_connect *connect_in_progress; + struct pending_connect *pending_connect; }; struct manager { @@ -103,7 +103,6 @@ struct manager { GSList *headset_list; }; - static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; @@ -279,10 +278,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) gsize free_space; GIOError err; - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); + if (cond & G_IO_NVAL) return FALSE; - } if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; @@ -368,16 +365,14 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) assert(amanager != NULL); - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); + if (cond & G_IO_NVAL) return FALSE; - } if (cond & (G_IO_HUP | G_IO_ERR)) { error("Hangup or error on rfcomm server socket"); g_io_channel_close(chan); - amanager->server_sk = NULL; - return TRUE; + raise(SIGTERM); + return FALSE; } srv_sk = g_io_channel_unix_get_fd(chan); @@ -417,6 +412,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) } g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); + g_io_channel_unref(hs->rfcomm); ba2str(&addr.rc_bdaddr, hs_address); @@ -481,14 +477,13 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond gsize bytes_written, total_bytes_written; GIOError err; - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); + if (cond & G_IO_NVAL) return FALSE; - } if (cond & (G_IO_HUP | G_IO_ERR)) { error("Audio connection got disconnected"); g_io_channel_close(chan); + g_io_channel_unref(hs->sco); hs->sco = NULL; if (hs->audio_output) { g_io_channel_close(hs->audio_output); @@ -536,13 +531,11 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, DBusMessage *reply; socklen_t len; - assert(hs != NULL && hs->connect_in_progress != NULL && - hs->sco == NULL && hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); - - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); + if (cond & G_IO_NVAL) return FALSE; - } + + assert(hs != NULL && hs->pending_connect != NULL && + hs->sco == NULL && hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); sk = g_io_channel_unix_get_fd(chan); @@ -562,13 +555,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, debug("SCO socket opened for headset %s", hs->object_path); hs->sco = chan; - hs->connect_in_progress->io = NULL; + hs->pending_connect->io = NULL; flags = hs->audio_output ? G_IO_IN : 0; g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); - if (hs->connect_in_progress->msg) { - reply = dbus_message_new_method_return(hs->connect_in_progress->msg); + if (hs->pending_connect->msg) { + reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) send_message_and_unref(connection, reply); } @@ -577,8 +570,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, if (hs->audio_input) g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_PLAYING; hs_signal(hs, "Playing"); @@ -586,10 +579,12 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - if (hs->connect_in_progress) { - err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + if (hs->pending_connect) { + err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); + if (hs->pending_connect->io) + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; } assert(hs->rfcomm); @@ -604,15 +599,13 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he int sk, ret, err; socklen_t len; - assert(hs != NULL && hs->connect_in_progress != NULL && + if (cond & G_IO_NVAL) + return FALSE; + + assert(hs != NULL && hs->pending_connect != NULL && hs->rfcomm == NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); - return FALSE; - } - sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); @@ -630,6 +623,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he ba2str(&hs->bda, hs_address); hs->rfcomm = chan; + hs->pending_connect->io = NULL; hs->state = HEADSET_STATE_CONNECTED; hs_signal(hs, "Connected"); @@ -638,23 +632,25 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); - if (hs->connect_in_progress->msg) { + if (hs->pending_connect->msg) { DBusMessage *reply; - reply = dbus_message_new_method_return(hs->connect_in_progress->msg); + reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) send_message_and_unref(connection, reply); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; } return FALSE; failed: - if (hs->connect_in_progress) { - err_connect_failed(hs->connect_in_progress->conn, hs->connect_in_progress->msg, err); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + if (hs->pending_connect) { + err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); + if (hs->pending_connect->io) + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; } hs->state = HEADSET_STATE_DISCONNECTED; @@ -668,12 +664,12 @@ static int rfcomm_connect(struct headset *hs, int *err) char address[18]; int sk; - assert(hs != NULL && hs->connect_in_progress != NULL && + assert(hs != NULL && hs->pending_connect != NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); ba2str(&hs->bda, address); - debug("Connecting to %s channel %d", address, hs->connect_in_progress->ch); + debug("Connecting to %s channel %d", address, hs->pending_connect->ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { @@ -703,14 +699,13 @@ static int rfcomm_connect(struct headset *hs, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, &hs->bda); - addr.rc_channel = hs->connect_in_progress->ch; + addr.rc_channel = hs->pending_connect->ch; - hs->connect_in_progress->io = g_io_channel_unix_new(sk); - if (!hs->connect_in_progress->io) { + hs->pending_connect->io = g_io_channel_unix_new(sk); + if (!hs->pending_connect->io) { error("channel_unix_new failed in rfcomm connect"); goto failed; } - g_io_channel_set_close_on_unref(hs->connect_in_progress->io, TRUE); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -722,22 +717,18 @@ static int rfcomm_connect(struct headset *hs, int *err) debug("Connect in progress"); - g_io_add_watch(hs->connect_in_progress->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, hs); + g_io_add_watch(hs->pending_connect->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, hs); } else { debug("Connect succeeded with first try"); - rfcomm_connect_cb(hs->connect_in_progress->io, G_IO_OUT, hs); + rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, hs); } return 0; failed: - if (hs->connect_in_progress->io) { - g_io_channel_close(hs->connect_in_progress->io); - hs->connect_in_progress->io = NULL; - } else { - if (sk >= 0) - close(sk); - } + if (!hs->pending_connect->io && sk >= 0) + close(sk); + return -1; } @@ -932,8 +923,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct headset *hs = data; struct pending_connect *c; - assert(hs != NULL && hs->connect_in_progress && !hs->rfcomm); - c = hs->connect_in_progress; + assert(hs != NULL && hs->pending_connect && !hs->rfcomm); + c = hs->pending_connect; reply = dbus_pending_call_steal_reply(call); @@ -1005,8 +996,8 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; } @@ -1028,18 +1019,21 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); hs->rfcomm = NULL; } - if (hs->connect_in_progress) { - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + if (hs->pending_connect) { + if (hs->pending_connect->io) + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; } hs->state = HEADSET_STATE_DISCONNECTED; ba2str(&hs->bda, hs_address); - info("Disconnected from %s, %s", &hs_address, hs->object_path ? hs->object_path : "null"); + info("Disconnected from %s, %s", &hs_address, hs->object_path); hs_signal(hs, "Disconnected"); @@ -1061,8 +1055,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) dbus_uint32_t handle; int array_len; - assert(hs != NULL && hs->connect_in_progress); - c = hs->connect_in_progress; + assert(hs != NULL && hs->pending_connect); + c = hs->pending_connect; reply = dbus_pending_call_steal_reply(call); @@ -1158,29 +1152,29 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) error("This headset has not been audiothorized"); } - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->connect_in_progress) { + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) { error("Already connected"); return DBUS_HANDLER_RESULT_HANDLED; } - hs->connect_in_progress = g_try_new0(struct pending_connect, 1); - if (!hs->connect_in_progress) { + hs->pending_connect = g_try_new0(struct pending_connect, 1); + if (!hs->pending_connect) { error("Out of memory when allocating new struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; - hs->connect_in_progress->conn = dbus_connection_ref(connection); - hs->connect_in_progress->msg = msg ? dbus_message_ref(msg) : NULL; + hs->pending_connect->conn = dbus_connection_ref(connection); + hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL; msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { error("Could not create a new dbus message"); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -1193,8 +1187,8 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; dbus_message_unref(msg); return err_connect_failed(connection, msg, EIO); @@ -1318,20 +1312,19 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(connection, msg); /* FIXME: in progress error? */ - if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->connect_in_progress) + if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) return err_already_connected(connection, msg); if (hs->sco) return err_already_connected(connection, msg); - hs->connect_in_progress = malloc(sizeof(struct pending_connect)); - if (!hs->connect_in_progress) + hs->pending_connect = g_try_new0(struct pending_connect, 1); + if (!hs->pending_connect) return DBUS_HANDLER_RESULT_NEED_MEMORY; hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - memset(hs->connect_in_progress, 0, sizeof(struct pending_connect)); - c = hs->connect_in_progress; + c = hs->pending_connect; c->conn = dbus_connection_ref(connection); c->msg = msg ? dbus_message_ref(msg) : NULL; @@ -1350,8 +1343,6 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_NEED_MEMORY; } - g_io_channel_set_close_on_unref(c->io, TRUE); - memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; bacpy(&addr.sco_bdaddr, BDADDR_ANY); @@ -1391,9 +1382,9 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) return 0; failed: - if (hs->connect_in_progress) { - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + if (hs->pending_connect) { + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; } return DBUS_HANDLER_RESULT_HANDLED; } @@ -1411,9 +1402,10 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_NEED_MEMORY; } - if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->connect_in_progress) { - pending_connect_free(hs->connect_in_progress); - hs->connect_in_progress = NULL; + if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_CONNECTED; } @@ -1643,8 +1635,6 @@ gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t c return FALSE; } - g_io_channel_set_close_on_unref(amanager->server_sk, TRUE); - g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); return TRUE; -- cgit From ae3e54003a6e36749ac23a4e0cbecf49ba561656 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 08:42:09 +0000 Subject: Use -d for running as an external service --- audio/headset.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 79c85084..b48c2986 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1861,7 +1861,7 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt; - while ((opt = getopt(argc, argv, "c:o:i:dr")) != EOF) { + while ((opt = getopt(argc, argv, "c:o:i:d")) != EOF) { switch (opt) { case 'c': opt_channel = strtol(optarg, NULL, 0); @@ -1877,9 +1877,6 @@ int main(int argc, char *argv[]) case 'd': enable_debug(); - break; - - case 'r': register_svc = TRUE; break; -- cgit From c2c130dfca7b749ecc72a45836b6d8beaa299654 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 08:46:39 +0000 Subject: Remove invalid g_io_channel_unref --- audio/headset.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b48c2986..f8feed4f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -412,7 +412,6 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) } g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); - g_io_channel_unref(hs->rfcomm); ba2str(&addr.rc_bdaddr, hs_address); -- cgit From e41652b93c75dd524095c9a8255ff4db265c15f1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 Feb 2007 08:56:24 +0000 Subject: Change option for standalone mode --- audio/headset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index f8feed4f..1fcdf6c2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1860,7 +1860,7 @@ int main(int argc, char *argv[]) struct sigaction sa; int opt; - while ((opt = getopt(argc, argv, "c:o:i:d")) != EOF) { + while ((opt = getopt(argc, argv, "c:o:i:ds")) != EOF) { switch (opt) { case 'c': opt_channel = strtol(optarg, NULL, 0); @@ -1876,6 +1876,9 @@ int main(int argc, char *argv[]) case 'd': enable_debug(); + break; + + case 's': register_svc = TRUE; break; -- cgit From 3b339295a07f906bdee4a7d5df1eb89d21bef8f0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 17:53:16 +0000 Subject: More cleanup and fixes --- audio/headset.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1fcdf6c2..a0ada250 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -278,13 +278,15 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) gsize free_space; GIOError err; + debug("rfcomm_io_cb: entered"); + if (cond & G_IO_NVAL) return FALSE; if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; - err = g_io_channel_read(chan, (gchar *)buf, sizeof(buf) - 1, &bytes_read); + err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read); if (err != G_IO_ERROR_NONE) goto failed; @@ -392,12 +394,12 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) close(cli_sk); return TRUE; } + + audio_manager_add_headset(amanager, hs); } /* audio_headset_authorize(hs); */ - debug("Incoming connection on the server_sk for object %s", hs->object_path); - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); close(cli_sk); @@ -411,7 +413,8 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - g_io_add_watch(hs->rfcomm, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); + g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, hs); ba2str(&addr.rc_bdaddr, hs_address); @@ -629,7 +632,8 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he debug("Connected to %s", hs_address); - g_io_add_watch(chan, G_IO_IN, (GIOFunc) rfcomm_io_cb, hs); + g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, hs); if (hs->pending_connect->msg) { DBusMessage *reply; @@ -1147,10 +1151,6 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) assert(hs != NULL); - if (hs->state == HEADSET_STATE_UNAUTHORIZED) { - error("This headset has not been audiothorized"); - } - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) { error("Already connected"); return DBUS_HANDLER_RESULT_HANDLED; @@ -1634,7 +1634,9 @@ gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t c return FALSE; } - g_io_add_watch(amanager->server_sk, G_IO_IN, (GIOFunc) server_io_cb, amanager); + g_io_add_watch(amanager->server_sk, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) server_io_cb, amanager); return TRUE; } @@ -1704,6 +1706,7 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, return error_reply(connection, msg, "org.bluez.Error.Failed", "Unable to create new headset object"); + audio_manager_add_headset(amanager, hs); } object_path = hs->object_path; -- cgit From d6964e5a5b4f0f3b5bc859b11a1352a06e313ecd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 18:27:33 +0000 Subject: Be sure to always reply to method calls --- audio/headset.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a0ada250..da4350ce 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -530,7 +530,6 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) { int ret, sk, err, flags; - DBusMessage *reply; socklen_t len; if (cond & G_IO_NVAL) @@ -563,6 +562,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); if (hs->pending_connect->msg) { + DBusMessage *reply; + reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) send_message_and_unref(connection, reply); @@ -635,12 +636,15 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, hs); - if (hs->pending_connect->msg) { - DBusMessage *reply; + if (hs->pending_connect) { + if (hs->pending_connect->msg) { + DBusMessage *reply; + + reply = dbus_message_new_method_return(hs->pending_connect->msg); + if (reply) + send_message_and_unref(hs->pending_connect->conn, reply); + } - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(connection, reply); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; } @@ -1029,6 +1033,10 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) if (hs->pending_connect) { if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); + if (hs->pending_connect->msg) + err_connect_failed(hs->pending_connect->conn, + hs->pending_connect->msg, + EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; } @@ -1317,13 +1325,12 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (hs->sco) return err_already_connected(connection, msg); - hs->pending_connect = g_try_new0(struct pending_connect, 1); - if (!hs->pending_connect) + c = g_try_new0(struct pending_connect, 1); + if (!c) return DBUS_HANDLER_RESULT_NEED_MEMORY; hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - c = hs->pending_connect; c->conn = dbus_connection_ref(connection); c->msg = msg ? dbus_message_ref(msg) : NULL; @@ -1378,13 +1385,14 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) sco_connect_cb(c->io, G_IO_OUT, hs); } + hs->pending_connect = c; + return 0; failed: - if (hs->pending_connect) { - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - } + if (c) + pending_connect_free(c); + return DBUS_HANDLER_RESULT_HANDLED; } @@ -1403,6 +1411,10 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { g_io_channel_close(hs->pending_connect->io); + if (hs->pending_connect->msg) + err_connect_failed(hs->pending_connect->conn, + hs->pending_connect->msg, + EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_CONNECTED; -- cgit From 661d9a5e7017bf6a79b62ff5d22d8eaec5fac7c6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 19:49:03 +0000 Subject: Add missing calls to dbus_pending_call_unref --- audio/headset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index da4350ce..0956208a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -995,6 +995,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); + dbus_pending_call_unref(call); return; @@ -1003,6 +1004,7 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); + dbus_pending_call_unref(call); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; @@ -1138,8 +1140,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL); dbus_message_unref(msg); - dbus_message_unref(reply); + dbus_pending_call_unref(call); return; @@ -1147,6 +1149,7 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); + dbus_pending_call_unref(call); hs_disconnect(hs, NULL); } -- cgit From 9332d6301f60d708d94e5956fe3981576f6dfde2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 22:44:29 +0000 Subject: More cleanup --- audio/headset.c | 225 ++++++++++++++++++++++++++------------------------------ 1 file changed, 104 insertions(+), 121 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0956208a..9ce11d10 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -107,12 +107,12 @@ static DBusConnection *connection = NULL; static GMainLoop *main_loop = NULL; struct manager *audio_manager_new(DBusConnection *conn); -void audio_manager_free(struct manager *amanager); -struct headset *audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda); -void audio_manager_add_headset(struct manager *amanager, struct headset *hs); -gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t chan); -static DBusHandlerResult am_get_default_headset(struct manager *amanager, DBusMessage *msg); -static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *msg); +void audio_manager_free(struct manager *manager); +struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda); +void audio_manager_add_headset(struct manager *manager, struct headset *hs); +gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan); +static DBusHandlerResult am_get_default_headset(struct manager *manager, DBusMessage *msg); +static DBusHandlerResult am_create_headset(struct manager *manager, DBusMessage *msg); struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); void audio_headset_unref(struct headset *hs); @@ -139,7 +139,7 @@ static void pending_connect_free(struct pending_connect *c) dbus_message_unref(c->msg); if (c->conn) dbus_connection_unref(c->conn); - free(c); + g_free(c); } static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, @@ -269,9 +269,9 @@ static int parse_headset_event(const char *buf, char *rsp, int rsp_len) return rv; } -static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, + struct headset *hs) { - struct headset *hs = data; unsigned char buf[BUF_SIZE]; char *cr; gsize bytes_read = 0; @@ -356,16 +356,16 @@ failed: return FALSE; } -static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, + struct manager *manager) { int srv_sk, cli_sk; struct sockaddr_rc addr; socklen_t size; char hs_address[18]; struct headset *hs = NULL; - struct manager *amanager = (struct manager *) data; - assert(amanager != NULL); + assert(manager != NULL); if (cond & G_IO_NVAL) return FALSE; @@ -386,7 +386,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - hs = audio_manager_find_headset_by_bda(amanager, &addr.rc_bdaddr); + hs = audio_manager_find_headset_by_bda(manager, &addr.rc_bdaddr); if (!hs) { hs = audio_headset_new(connection, &addr.rc_bdaddr); if (!hs) { @@ -395,7 +395,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - audio_manager_add_headset(amanager, hs); + audio_manager_add_headset(manager, hs); } /* audio_headset_authorize(hs); */ @@ -434,27 +434,15 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin gsize bytes_written, total_bytes_written; GIOError err; - if (!hs || !hs->sco) { - error("The headset is invalid or does not have a SCO connection up"); - audio_headset_close_input(hs); + if (cond & G_IO_NVAL) return FALSE; - } - if (cond & G_IO_NVAL) { - g_io_channel_unref(chan); - return FALSE; - } - - if (cond & (G_IO_HUP | G_IO_ERR)) { - audio_headset_close_input(hs); - return FALSE; - } + if (cond & (G_IO_HUP | G_IO_ERR)) + goto failed; err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); - if (err != G_IO_ERROR_NONE) { - audio_headset_close_input(hs); - return FALSE; - } + if (err != G_IO_ERROR_NONE) + goto failed; total_bytes_written = bytes_written = 0; err = G_IO_ERROR_NONE; @@ -469,6 +457,26 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin }; return TRUE; + +failed: + audio_headset_close_input(hs); + return FALSE; +} + +static void close_sco(struct headset *hs) +{ + g_io_channel_close(hs->sco); + g_io_channel_unref(hs->sco); + hs->sco = NULL; + if (hs->audio_output) { + g_io_channel_unref(hs->audio_output); + hs->audio_output = NULL; + } + if (hs->audio_input) + audio_headset_close_input(hs); + assert(hs->rfcomm); + hs->state = HEADSET_STATE_CONNECTED; + hs_signal(hs, "Stopped"); } static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) @@ -482,20 +490,8 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond if (cond & G_IO_NVAL) return FALSE; - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Audio connection got disconnected"); - g_io_channel_close(chan); - g_io_channel_unref(hs->sco); - hs->sco = NULL; - if (hs->audio_output) { - g_io_channel_close(hs->audio_output); - hs->audio_output = NULL; - } - assert(hs->rfcomm); - hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Stopped"); - return FALSE; - } + if (cond & (G_IO_HUP | G_IO_ERR)) + goto disconn; if (!hs->audio_output && hs->output) audio_headset_open_output(hs, hs->output); @@ -503,10 +499,10 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); if (err != G_IO_ERROR_NONE) - return FALSE; + goto disconn; if (!hs->audio_output) { - error("no audio output"); + error("got %d bytes audio but have nowhere to write it", bytes_read); return TRUE; } @@ -524,6 +520,11 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond }; return TRUE; + +disconn: + error("Audio connection got disconnected"); + close_sco(hs); + return FALSE; } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, @@ -1423,14 +1424,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) hs->state = HEADSET_STATE_CONNECTED; } - if (hs->sco) { - g_io_channel_close(hs->sco); - hs->sco = NULL; - hs->state = HEADSET_STATE_CONNECTED; - } - - hs_signal(hs, "Stopped"); - hs->state = HEADSET_STATE_CONNECTED; + close_sco(hs); if (reply) send_message_and_unref(connection, reply); @@ -1516,7 +1510,7 @@ void audio_headset_unref(struct headset *hs) { assert(hs != NULL); - free(hs); + g_free(hs); } gboolean audio_headset_close_output(struct headset *hs) @@ -1526,6 +1520,7 @@ gboolean audio_headset_close_output(struct headset *hs) if (hs->audio_output == NULL) return FALSE; + g_io_channel_close(hs->audio_output); g_io_channel_unref(hs->audio_output); hs->audio_output = NULL; @@ -1541,8 +1536,8 @@ gboolean audio_headset_open_output(struct headset *hs, const char *output) audio_headset_close_output(hs); if (output && hs->output) { - free(hs->output); - hs->output = strdup(output); + g_free(hs->output); + hs->output = g_strdup(output); } assert(hs->output); @@ -1572,11 +1567,10 @@ gboolean audio_headset_close_input(struct headset *hs) if (hs->audio_input == NULL) return FALSE; + g_io_channel_close(hs->audio_input); g_io_channel_unref(hs->audio_input); hs->audio_input = NULL; - hs->state = HEADSET_STATE_CONNECTED; - return TRUE; } @@ -1586,12 +1580,10 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input) assert(hs != NULL); - audio_headset_close_input(hs); - /* we keep the input name, and NULL can be use to reopen */ if (input && hs->input) { - free(hs->input); - hs->input = strdup(input); + g_free(hs->input); + hs->input = g_strdup(input); } assert(hs->input); @@ -1609,18 +1601,16 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input) return FALSE; } - g_io_channel_set_close_on_unref(hs->audio_input, TRUE); - return TRUE; } -gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t chan) +gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) { int srv_sk; - assert(amanager != NULL); + assert(manager != NULL); - if (amanager->server_sk) { + if (manager->server_sk) { error("Server socket already created"); return FALSE; } @@ -1631,27 +1621,27 @@ gboolean audio_manager_create_headset_server(struct manager *amanager, uint8_t c return FALSE; } - if (!amanager->record_id) - amanager->record_id = add_ag_record(chan); + if (!manager->record_id) + manager->record_id = add_ag_record(chan); - if (!amanager->record_id) { + if (!manager->record_id) { error("Unable to register service record"); close(srv_sk); return FALSE; } - amanager->server_sk = g_io_channel_unix_new(srv_sk); - if (!amanager->server_sk) { + manager->server_sk = g_io_channel_unix_new(srv_sk); + if (!manager->server_sk) { error("Unable to allocate new GIOChannel"); - remove_ag_record(amanager->record_id); - amanager->record_id = 0; + remove_ag_record(manager->record_id); + manager->record_id = 0; close(srv_sk); return FALSE; } - g_io_add_watch(amanager->server_sk, + g_io_add_watch(manager->server_sk, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) server_io_cb, amanager); + (GIOFunc) server_io_cb, manager); return TRUE; } @@ -1663,27 +1653,27 @@ static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) return bacmp(&hs->bda, bda); } -struct headset *audio_manager_find_headset_by_bda(struct manager *amanager, const bdaddr_t *bda) +struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda) { GSList *elem; - assert(amanager); - elem = g_slist_find_custom(amanager->headset_list, bda, headset_bda_cmp); + assert(manager); + elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); return elem ? elem->data : NULL; } -void audio_manager_add_headset(struct manager *amanager, struct headset *hs) +void audio_manager_add_headset(struct manager *manager, struct headset *hs) { - assert(amanager && hs); + assert(manager && hs); - if (g_slist_find(amanager->headset_list, hs)) + if (g_slist_find(manager->headset_list, hs)) return; - amanager->headset_list = g_slist_append(amanager->headset_list, hs); + manager->headset_list = g_slist_append(manager->headset_list, hs); } -static DBusHandlerResult am_create_headset(struct manager *amanager, +static DBusHandlerResult am_create_headset(struct manager *manager, DBusMessage *msg) { const char *object_path; @@ -1693,7 +1683,7 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, DBusMessage *reply; DBusError derr; - if (!amanager) + if (!manager) return err_not_connected(connection, msg); dbus_error_init(&derr); @@ -1714,14 +1704,14 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, return DBUS_HANDLER_RESULT_NEED_MEMORY; str2ba(address, &bda); - hs = audio_manager_find_headset_by_bda(amanager, &bda); + hs = audio_manager_find_headset_by_bda(manager, &bda); if (!hs) { hs = audio_headset_new(connection, &bda); if (!hs) return error_reply(connection, msg, "org.bluez.Error.Failed", "Unable to create new headset object"); - audio_manager_add_headset(amanager, hs); + audio_manager_add_headset(manager, hs); } object_path = hs->object_path; @@ -1731,14 +1721,14 @@ static DBusHandlerResult am_create_headset(struct manager *amanager, return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_get_default_headset(struct manager *amanager, +static DBusHandlerResult am_get_default_headset(struct manager *manager, DBusMessage *msg) { DBusMessage *reply; char object_path[128]; const char *opath = object_path; - if (!amanager) + if (!manager) return err_not_connected(connection, msg); reply = dbus_message_new_method_return(msg); @@ -1756,7 +1746,7 @@ static DBusHandlerResult am_message(DBusConnection *conn, DBusMessage *msg, void *data) { const char *interface, *member; - struct manager *amanager = (struct manager *)data; + struct manager *manager = data; interface = dbus_message_get_interface(msg); member = dbus_message_get_member(msg); @@ -1769,10 +1759,10 @@ static DBusHandlerResult am_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (strcmp(member, "CreateHeadset") == 0) - return am_create_headset(amanager, msg); + return am_create_headset(manager, msg); if (strcmp(member, "DefaultHeadset") == 0) - return am_get_default_headset(amanager, msg); + return am_get_default_headset(manager, msg); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -1781,51 +1771,44 @@ static const DBusObjectPathVTable am_table = { .message_function = am_message, }; -struct manager* audio_manager_new(DBusConnection *conn) +struct manager *audio_manager_new(DBusConnection *conn) { - struct manager *amanager; - - amanager = malloc(sizeof(struct manager)); - - if (!amanager) { - error("Allocating new hs connection struct failed!"); - return NULL; - } + struct manager *manager; - memset(amanager, 0, sizeof(struct manager)); + manager = g_new0(struct manager, 1); if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, - &am_table, amanager)) { + &am_table, manager)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - free(amanager); + g_free(manager); return NULL; } - return amanager; + return manager; } -void audio_manager_free(struct manager* amanager) +void audio_manager_free(struct manager *manager) { - assert(amanager != NULL); + assert(manager != NULL); - if (amanager->record_id) { - remove_ag_record(amanager->record_id); - amanager->record_id = 0; + if (manager->record_id) { + remove_ag_record(manager->record_id); + manager->record_id = 0; } - if (amanager->server_sk) { - g_io_channel_unref(amanager->server_sk); - amanager->server_sk = NULL; + if (manager->server_sk) { + g_io_channel_unref(manager->server_sk); + manager->server_sk = NULL; } - if (amanager->headset_list) { - g_slist_foreach(amanager->headset_list, (GFunc) audio_headset_unref, - amanager); - g_slist_free(amanager->headset_list); - amanager->headset_list = NULL; + if (manager->headset_list) { + g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref, + manager); + g_slist_free(manager->headset_list); + manager->headset_list = NULL; } - free(amanager); + g_free(manager); } static gboolean register_service(const char *ident, const char *name, -- cgit From 650143b29ba46b74030563c94ef24caaa0f94ff2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 22:51:50 +0000 Subject: Cleanup headset event parsing functions --- audio/headset.c | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 9ce11d10..395136a7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -59,12 +59,12 @@ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" -struct pending_connect { - int ch; - DBusConnection *conn; - DBusMessage *msg; - GIOChannel *io; -}; +typedef enum { + HEADSET_EVENT_KEYPRESS, + HEADSET_EVENT_GAIN, + HEADSET_EVENT_UNKNOWN, + HEADSET_EVENT_INVALID +} headset_event_t; typedef enum { HEADSET_STATE_UNAUTHORIZED, @@ -75,6 +75,13 @@ typedef enum { HEADSET_STATE_PLAYING, } headset_state_t; +struct pending_connect { + int ch; + DBusConnection *conn; + DBusMessage *msg; + GIOChannel *io; +}; + struct headset { char object_path[128]; bdaddr_t bda; @@ -244,29 +251,26 @@ static void hs_signal(struct headset *hs, const char *name) send_message_and_unref(connection, signal); } -static int parse_headset_event(const char *buf, char *rsp, int rsp_len) +static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) { - int rv = 0; - printf("Received: %s\n", buf); /* Return an error if this is not a proper AT command */ if (strncmp(buf, "AT", 2)) { snprintf(rsp, rsp_len, "\r\nERROR\r\n"); - return rv; + return HEADSET_EVENT_INVALID; } buf += 2; - if (!strncmp(buf, "+CKPD", 5)) - rv = 0; - else if (!strncmp(buf, "+VG", 3)) - rv = 1; - snprintf(rsp, rsp_len, "\r\nOK\r\n"); - /* return 1 if gain event */ - return rv; + if (!strncmp(buf, "+CKPD", 5)) + return HEADSET_EVENT_KEYPRESS; + else if (!strncmp(buf, "+VG", 3)) + return HEADSET_EVENT_GAIN; + else + return HEADSET_EVENT_UNKNOWN; } static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, @@ -318,10 +322,21 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, memset(rsp, 0, sizeof(rsp)); /* FIXME: make a better parse function */ - if (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp)) == 1) + switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { + case HEADSET_EVENT_GAIN: hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); - else + break; + + case HEADSET_EVENT_KEYPRESS: hs_signal(hs, "AnswerRequested"); + break; + + case HEADSET_EVENT_INVALID: + case HEADSET_EVENT_UNKNOWN: + default: + debug("Unknown headset event"); + break; + } count = strlen(rsp); total_bytes_written = bytes_written = 0; -- cgit From f32ff2b9a45f486db8a7b3789a6be6ce766723df Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 23:07:00 +0000 Subject: Remove unecessary debug print --- audio/headset.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 395136a7..1112f5b4 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -282,8 +282,6 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gsize free_space; GIOError err; - debug("rfcomm_io_cb: entered"); - if (cond & G_IO_NVAL) return FALSE; -- cgit From 6439c43376df98aab3cfb9cdcde0c3c0bfd2a367 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 15 Feb 2007 23:54:47 +0000 Subject: Fix io watch flags for sco channel --- audio/headset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1112f5b4..3714989b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -572,7 +572,10 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs->sco = chan; hs->pending_connect->io = NULL; - flags = hs->audio_output ? G_IO_IN : 0; + flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; + if (hs->audio_output) + flags |= G_IO_IN; + g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); if (hs->pending_connect->msg) { -- cgit From 6a37133f8c7cecb74d84b4f4f81fea90892ae652 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 16 Feb 2007 17:35:30 +0000 Subject: Reset data buffer when disconnecting --- audio/headset.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 3714989b..489cb09c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1067,6 +1067,9 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) hs_signal(hs, "Disconnected"); + hs->data_start = 0; + hs->data_length = 0; + if (reply) send_message_and_unref(connection, reply); -- cgit From bc6af96cbd6f8b073c9049e812b6f4a082c66a81 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 16 Feb 2007 19:58:15 +0000 Subject: Disconnect SCO if RFCOMM gets disconnected --- audio/headset.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 489cb09c..107cab47 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -273,6 +273,23 @@ static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_l return HEADSET_EVENT_UNKNOWN; } +static void close_sco(struct headset *hs) +{ + g_io_channel_close(hs->sco); + g_io_channel_unref(hs->sco); + hs->sco = NULL; + if (hs->audio_output) { + g_io_channel_unref(hs->audio_output); + hs->audio_output = NULL; + } + if (hs->audio_input) + audio_headset_close_input(hs); + assert(hs->rfcomm); + hs->state = HEADSET_STATE_CONNECTED; + hs_signal(hs, "Stopped"); +} + + static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) { @@ -364,6 +381,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: + if (hs->sco) + close_sco(hs); hs_disconnect(hs, NULL); return FALSE; @@ -476,22 +495,6 @@ failed: return FALSE; } -static void close_sco(struct headset *hs) -{ - g_io_channel_close(hs->sco); - g_io_channel_unref(hs->sco); - hs->sco = NULL; - if (hs->audio_output) { - g_io_channel_unref(hs->audio_output); - hs->audio_output = NULL; - } - if (hs->audio_input) - audio_headset_close_input(hs); - assert(hs->rfcomm); - hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Stopped"); -} - static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct headset *hs = data; @@ -536,7 +539,8 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond disconn: error("Audio connection got disconnected"); - close_sco(hs); + if (hs->sco) + close_sco(hs); return FALSE; } -- cgit From 5cc41f165af628e6c3c70f3b89d205f544264dd3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 17 Feb 2007 07:15:25 +0000 Subject: headset service doesn't need a well known bus name --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 107cab47..6dd249c8 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1929,7 +1929,7 @@ int main(int argc, char *argv[]) main_loop = g_main_loop_new(NULL, FALSE); - connection = init_dbus("org.bluez.audio", NULL, NULL); + connection = init_dbus(NULL, NULL, NULL); if (!connection) { error("Connection to system bus failed"); g_main_loop_unref(main_loop); -- cgit From 3a6e17b6aeaa92d27c1c7688d494557043e9ee3c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 25 Feb 2007 20:13:24 +0000 Subject: First part of authorization support for the headset service --- audio/headset.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 6dd249c8..105efa09 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -388,14 +388,52 @@ failed: return FALSE; } +static void auth_callback(DBusPendingCall *call, void *data) +{ + struct headset *hs = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + 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"); + //send_cancel(); + } + dbus_error_free(&err); + g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); + hs->rfcomm = NULL; + } else { + char hs_address[18]; + + g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, hs); + + ba2str(&hs->bda, hs_address); + + debug("Accepted connection from %s for %s", hs_address, hs->object_path); + + hs->state = HEADSET_STATE_CONNECTED; + hs_signal(hs, "Connected"); + } + + dbus_message_unref(reply); + dbus_pending_call_unref(call); +} + static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, struct manager *manager) { int srv_sk, cli_sk; struct sockaddr_rc addr; socklen_t size; - char hs_address[18]; + char hs_address[18], *address = hs_address; + const char *uuid = ""; struct headset *hs = NULL; + DBusMessage *auth; + DBusPendingCall *pending; assert(manager != NULL); @@ -430,8 +468,6 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, audio_manager_add_headset(manager, hs); } - /* audio_headset_authorize(hs); */ - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); close(cli_sk); @@ -445,15 +481,35 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; } - g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocat new RequestAuthorization method call"); + goto failed; + } + + ba2str(&hs->bda, hs_address); - ba2str(&addr.rc_bdaddr, hs_address); + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - debug("Accepted connection from %s, %s", hs_address, hs->object_path); + if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { + error("Sending of authorization request failed"); + goto failed; + } - hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Connected"); + dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); + + dbus_message_unref(auth); + + return TRUE; + +failed: + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); + hs->rfcomm = NULL; + } return TRUE; } -- cgit From 81bfe1e51bc7718ac0568572b7cec93b79fa7350 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 25 Feb 2007 20:50:57 +0000 Subject: Fix usage of dbus_pending_call_unref --- audio/headset.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 105efa09..4b7228ce 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -420,7 +420,6 @@ static void auth_callback(DBusPendingCall *call, void *data) } dbus_message_unref(reply); - dbus_pending_call_unref(call); } static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, @@ -499,7 +498,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, } dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); - + dbus_pending_call_unref(pending); dbus_message_unref(auth); return TRUE; @@ -1072,7 +1071,6 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - dbus_pending_call_unref(call); return; @@ -1081,7 +1079,6 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); - dbus_pending_call_unref(call); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; @@ -1219,9 +1216,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data) } dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL); + dbus_pending_call_unref(pending); dbus_message_unref(msg); dbus_message_unref(reply); - dbus_pending_call_unref(call); return; @@ -1229,7 +1226,6 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - dbus_pending_call_unref(call); hs_disconnect(hs, NULL); } @@ -1285,6 +1281,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) } dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); + dbus_pending_call_unref(pending); dbus_message_unref(msg); return DBUS_HANDLER_RESULT_HANDLED;; -- cgit From 53031087fce5c0c0a80f7a9c51e36e53333935a4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 25 Feb 2007 20:53:01 +0000 Subject: Send CancelAuthorizationRequest when timing out waiting for a reply from hcid --- audio/headset.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 4b7228ce..81d6ac7c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -388,6 +388,28 @@ failed: return FALSE; } +static void send_cancel_auth(struct headset *hs) +{ + DBusMessage *cancel; + char addr[18], *address = addr; + const char *uuid = ""; + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(&hs->bda, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); +} + static void auth_callback(DBusPendingCall *call, void *data) { struct headset *hs = data; @@ -399,7 +421,7 @@ static void auth_callback(DBusPendingCall *call, void *data) error("Access denied: %s", err.message); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - //send_cancel(); + send_cancel_auth(hs); } dbus_error_free(&err); g_io_channel_close(hs->rfcomm); -- cgit From 60159d1c8e01f5a89958d3b07348609f945f9a1d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 27 Feb 2007 09:00:56 +0000 Subject: Require all incoming connections to be secure --- audio/headset.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 81d6ac7c..55abd868 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -839,7 +839,7 @@ failed: static int server_socket(uint8_t *channel) { - int sock; + int sock, lm; struct sockaddr_rc addr; socklen_t sa_len; @@ -849,6 +849,13 @@ static int server_socket(uint8_t *channel) return -1; } + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s", strerror(errno), errno); + close(sock); + return -1; + } + memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, BDADDR_ANY); -- cgit From 76c9ccdc6aa16e923108e221e150c0c7fc43079f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 27 Feb 2007 09:06:41 +0000 Subject: Fix format string --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 55abd868..abb0e1e9 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -851,7 +851,7 @@ static int server_socket(uint8_t *channel) lm = RFCOMM_LM_SECURE; if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s", strerror(errno), errno); + error("server setsockopt: %s (%d)", strerror(errno), errno); close(sock); return -1; } -- cgit From 2a093f56f87ab1ddec84f869c7941de96ee0bb0f Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Thu, 8 Mar 2007 14:49:32 +0000 Subject: flesh out headset object --- audio/audio-api.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 436296a7..92858823 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -51,3 +51,23 @@ Methods string GetAddress() void Connect() void Disconnect() + + uint16 GetSpeakerGain() + + uint16 GetMicrophoneGain() + + void SetSpeakerGain(uint16 gain) + + Gain is 0..15 + + void SetMicrophoneGain(uint16 gain) + + Gain is 0..15 + +Signals void SpeakerGainChanged(uint16 gain) + + The speaker gain changed. + + void MicrophoneGainChanged(uint16 gain) + + The microphone gain changed. -- cgit From bdc24681f71ab07cbe548dd006a4624dda669fd1 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Thu, 8 Mar 2007 15:30:48 +0000 Subject: indicatecall --- audio/audio-api.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 92858823..81c98aef 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -64,6 +64,16 @@ Methods string GetAddress() Gain is 0..15 + void IndicateCall() + + Indicate an incoming call on the headset + connected to the stream. Will continue to + ring the headset about every 3 seconds. + + void CancelCall() + + Cancel the incoming call indication. + Signals void SpeakerGainChanged(uint16 gain) The speaker gain changed. @@ -71,3 +81,11 @@ Signals void SpeakerGainChanged(uint16 gain) void MicrophoneGainChanged(uint16 gain) The microphone gain changed. + + void CallIndicated() + + An incoming call was indicated. + + void CallCanceled() + + Incoming call was canceled. -- cgit From 850927098605ff169bcc0b576ebd7e0ace963595 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 9 Mar 2007 22:32:47 +0000 Subject: Add skeleton for manager interface --- audio/Makefile.am | 2 +- audio/main.c | 8 +++++++- audio/manager.c | 37 +++++++++++++++++++++++++++++++++++++ audio/manager.h | 25 +++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 audio/manager.c create mode 100644 audio/manager.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 5fbbcd21..63f338b7 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -4,7 +4,7 @@ servicedir = $(libdir)/bluetooth noinst_PROGRAMS = bluetoothd-service-audio \ bluetoothd-service-headset -bluetoothd_service_audio_SOURCES = main.c +bluetoothd_service_audio_SOURCES = main.c manager.h manager.c bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ diff --git a/audio/main.c b/audio/main.c index c04bb251..2f3a1fa0 100644 --- a/audio/main.c +++ b/audio/main.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2005-2006 Marcel Holtmann + * Copyright (C) 2004-2007 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,13 @@ #include #include +#include "manager.h" + int main(int argc, char *argv[]) { + audio_init(); + + audio_exit(); + return 0; } diff --git a/audio/manager.c b/audio/manager.c new file mode 100644 index 00000000..7c7a92c0 --- /dev/null +++ b/audio/manager.c @@ -0,0 +1,37 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "manager.h" + +int audio_init(void) +{ + return 0; +} + +void audio_exit(void) +{ +} diff --git a/audio/manager.h b/audio/manager.h new file mode 100644 index 00000000..3f2988f9 --- /dev/null +++ b/audio/manager.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +int audio_init(void); +void audio_exit(void); -- cgit From 03cef9b2edbb0be870f3e75aad6ec66ed996b3d0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 21 Mar 2007 18:15:29 +0000 Subject: Manager methods belong in the Manager interface --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index abb0e1e9..1a0013a7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1860,7 +1860,7 @@ static DBusHandlerResult am_message(DBusConnection *conn, !strcmp("Introspect", member)) return simple_introspect(conn, msg, data); - if (strcmp(interface, "org.bluez.audio.Headset") != 0) + if (strcmp(interface, "org.bluez.audio.Manager") != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (strcmp(member, "CreateHeadset") == 0) -- cgit From 221adbabd69654d2c35288692683ad3ccb49fdf2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 21 Mar 2007 18:42:27 +0000 Subject: Fix error check for failed connect --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1a0013a7..ad97172f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1183,7 +1183,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); if (c->msg) { - if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectFailed")) + if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) err_connect_failed(c->conn, c->msg, EHOSTDOWN); else err_not_supported(c->conn, c->msg); -- cgit From f1de5c126e4f7fb10de9f2dac688887018312c51 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 1 Apr 2007 20:36:31 +0000 Subject: Implement IsConnected method --- audio/headset.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index ad97172f..d1ee5032 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1162,6 +1162,30 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg) +{ + DBusMessage *reply; + dbus_bool_t connected; + + assert(hs); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (hs->state >= HEADSET_STATE_CONNECTED) + connected = TRUE; + else + connected = FALSE; + + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static void get_handles_reply(DBusPendingCall *call, void *data) { DBusMessage *msg = NULL, *reply; @@ -1561,6 +1585,9 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(member, "Disconnect") == 0) return hs_disconnect(hs, msg); + if (strcmp(member, "IsConnected") == 0) + return hs_is_connected(hs, msg); + if (strcmp(member, "IndicateCall") == 0) return hs_ring(hs, msg); -- cgit From 7af87ae5d346aed8ba1a6a9ae66f6dae0a393915 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 1 Apr 2007 20:42:07 +0000 Subject: Get rid of some unnecessary static function prototypes --- audio/headset.c | 73 +++++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index d1ee5032..7a01806f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -118,8 +118,6 @@ void audio_manager_free(struct manager *manager); struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda); void audio_manager_add_headset(struct manager *manager, struct headset *hs); gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan); -static DBusHandlerResult am_get_default_headset(struct manager *manager, DBusMessage *msg); -static DBusHandlerResult am_create_headset(struct manager *manager, DBusMessage *msg); struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); void audio_headset_unref(struct headset *hs); @@ -129,14 +127,7 @@ gboolean audio_headset_close_output(struct headset *hs); gboolean audio_headset_open_output(struct headset *hs, const char *audio_output); GIOError audio_headset_send_ring(struct headset *hs); -static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg); static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); -static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg); -static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg); -static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg); -static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg); -static void hs_signal(struct headset *hs, const char *name); -static void hs_signal_gain_setting(struct headset *hs, const char *buf); static void pending_connect_free(struct pending_connect *c) { @@ -1113,6 +1104,38 @@ failed: hs->state = HEADSET_STATE_DISCONNECTED; } +static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) +{ + DBusMessage *reply = NULL; + + if (!hs || !hs->sco) + return err_not_connected(connection, msg); + + if (msg) { + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { + g_io_channel_close(hs->pending_connect->io); + if (hs->pending_connect->msg) + err_connect_failed(hs->pending_connect->conn, + hs->pending_connect->msg, + EINTR); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; + hs->state = HEADSET_STATE_CONNECTED; + } + + close_sco(hs); + + if (reply) + send_message_and_unref(connection, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) { DBusMessage *reply = NULL; @@ -1529,38 +1552,6 @@ failed: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) -{ - DBusMessage *reply = NULL; - - if (!hs || !hs->sco) - return err_not_connected(connection, msg); - - if (msg) { - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { - g_io_channel_close(hs->pending_connect->io); - if (hs->pending_connect->msg) - err_connect_failed(hs->pending_connect->conn, - hs->pending_connect->msg, - EINTR); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - hs->state = HEADSET_STATE_CONNECTED; - } - - close_sco(hs); - - if (reply) - send_message_and_unref(connection, reply); - - return DBUS_HANDLER_RESULT_HANDLED; -} - static DBusHandlerResult hs_message(DBusConnection *conn, DBusMessage *msg, void *data) { -- cgit From c40762547147401db6eed6d64db09744e5668dda Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 1 Apr 2007 20:44:07 +0000 Subject: Add IsConnected() to dbus-api.txt --- audio/audio-api.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 81c98aef..e123689e 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -52,6 +52,8 @@ Methods string GetAddress() void Disconnect() + boolean IsConnected() + uint16 GetSpeakerGain() uint16 GetMicrophoneGain() -- cgit From c0be1b30ea748331adfccadaadb49193f407bdd4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 1 Apr 2007 21:02:31 +0000 Subject: Make headset service installable --- audio/Makefile.am | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 63f338b7..4de6d59f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,18 +1,27 @@ -servicedir = $(libdir)/bluetooth +if HEADSETSERVICE +if CONFIGFILES +confdir = $(sysconfdir)/bluetooth -noinst_PROGRAMS = bluetoothd-service-audio \ - bluetoothd-service-headset +conf_DATA = headset.service +endif -bluetoothd_service_audio_SOURCES = main.c manager.h manager.c +servicedir = $(libdir)/bluetooth -bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ - @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +service_PROGRAMS = bluetoothd-service-headset bluetoothd_service_headset_SOURCES = headset.c bluetoothd_service_headset_LDADD = $(top_builddir)/common/libhelper.a \ - @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ + @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +endif + +noinst_PROGRAMS = bluetoothd-service-audio + +bluetoothd_service_audio_SOURCES = main.c manager.h manager.c + +bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ + @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @SBC_CFLAGS@ -- cgit From e7bd84e89e0af0cd2c2390bf7a173f90a473fc62 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 4 Apr 2007 09:39:23 +0000 Subject: Add skeleton for generic ALSA plugins --- audio/Makefile.am | 16 +++++++++++++++- audio/ctl_bluetooth.c | 26 ++++++++++++++++++++++++++ audio/pcm_bluetooth.c | 26 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 audio/ctl_bluetooth.c create mode 100644 audio/pcm_bluetooth.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 4de6d59f..1f8d70d7 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -16,6 +16,20 @@ bluetoothd_service_headset_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ endif +if ALSA +alsadir = $(libdir)/alsa-lib + +alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la + +libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c +libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libasound_module_pcm_bluetooth_la_LIBADD = @ALSA_LIBS@ + +libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c +libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ +endif + noinst_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c manager.h manager.c @@ -23,7 +37,7 @@ bluetoothd_service_audio_SOURCES = main.c manager.h manager.c bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @SBC_CFLAGS@ +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS@ INCLUDES = -I$(top_srcdir)/common diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c new file mode 100644 index 00000000..7e9755b8 --- /dev/null +++ b/audio/ctl_bluetooth.c @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c new file mode 100644 index 00000000..7e9755b8 --- /dev/null +++ b/audio/pcm_bluetooth.c @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 -- cgit From 31a3adbb73dd4b629b19d87157a1731758ddcaa1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 4 Apr 2007 09:41:19 +0000 Subject: Add example asound.conf file --- audio/Makefile.am | 2 +- audio/asound.conf | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 audio/asound.conf (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 1f8d70d7..16c4f245 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -41,6 +41,6 @@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS INCLUDES = -I$(top_srcdir)/common -EXTRA_DIST = headset.service audio-api.txt +EXTRA_DIST = headset.service audio-api.txt asound.conf MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/asound.conf b/audio/asound.conf new file mode 100644 index 00000000..ec97d46f --- /dev/null +++ b/audio/asound.conf @@ -0,0 +1,7 @@ +pcm.headset { + type bluetooth +} + +ctl.headset { + type bluetooth +} -- cgit From 5ad9e0ec87dd078dc2a4e70967f1e9ace1ebf43f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 4 Apr 2007 09:51:11 +0000 Subject: Add basic plugin code --- audio/ctl_bluetooth.c | 12 ++++++++++++ audio/pcm_bluetooth.c | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 7e9755b8..511a1b24 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -24,3 +24,15 @@ #ifdef HAVE_CONFIG_H #include #endif + +#include +#include + +SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) +{ + printf("Bluetooth control plugin\n"); + + return -EIO; +} + +SND_CTL_PLUGIN_SYMBOL(bluetooth); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 7e9755b8..b9f4f02c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -24,3 +24,15 @@ #ifdef HAVE_CONFIG_H #include #endif + +#include +#include + +SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) +{ + printf("Bluetooth PCM plugin\n"); + + return -EIO; +} + +SND_PCM_PLUGIN_SYMBOL(bluetooth); -- cgit From e046ec624abb9fda8613af2079e51152f506bd7a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Apr 2007 11:28:35 +0000 Subject: Add skeletons for missing methods --- audio/headset.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 7a01806f..fa4d68c2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -107,6 +107,7 @@ struct headset { struct manager { GIOChannel *server_sk; uint32_t record_id; + int default_hs; GSList *headset_list; }; @@ -1844,6 +1845,18 @@ static DBusHandlerResult am_create_headset(struct manager *manager, return send_message_and_unref(connection, reply); } +static DBusHandlerResult am_create_headset(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_list_headsets(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + static DBusHandlerResult am_get_default_headset(struct manager *manager, DBusMessage *msg) { @@ -1858,13 +1871,20 @@ static DBusHandlerResult am_get_default_headset(struct manager *manager, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", 0); + snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", + manager->default_hs); dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } +static DBusHandlerResult am_change_default_headset(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + static DBusHandlerResult am_message(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1884,9 +1904,18 @@ static DBusHandlerResult am_message(DBusConnection *conn, if (strcmp(member, "CreateHeadset") == 0) return am_create_headset(manager, msg); + if (strcmp(member, "RemoveHeadset") == 0) + return am_remove_headset(manager, msg); + + if (strcmp(member, "ListHeadsets") == 0) + return am_list_headsets(manager, msg); + if (strcmp(member, "DefaultHeadset") == 0) return am_get_default_headset(manager, msg); + if (strcmp(member, "ChangeDefaultHeadset") == 0) + return am_change_default_headset(manager, msg); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -- cgit From e6f25afcdd988e0d40add0363592d78c3685dd00 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 4 Apr 2007 13:20:32 +0000 Subject: Fix copy-pase typo in function name --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index fa4d68c2..81d25550 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1845,7 +1845,7 @@ static DBusHandlerResult am_create_headset(struct manager *manager, return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_create_headset(struct manager *manager, +static DBusHandlerResult am_remove_headset(struct manager *manager, DBusMessage *msg) { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -- cgit From fa83abed5f41a567175c9b734fba55fa979f455a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Apr 2007 19:56:52 +0000 Subject: Only provide generic audio service --- audio/Makefile.am | 21 +++++++-------------- audio/audio.service | 4 ++++ audio/headset.service | 4 ---- 3 files changed, 11 insertions(+), 18 deletions(-) create mode 100644 audio/audio.service delete mode 100644 audio/headset.service (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 16c4f245..cbb27956 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,19 +1,19 @@ -if HEADSETSERVICE +if AUDIOSERVICE if CONFIGFILES confdir = $(sysconfdir)/bluetooth -conf_DATA = headset.service +conf_DATA = audio.service endif servicedir = $(libdir)/bluetooth -service_PROGRAMS = bluetoothd-service-headset +service_PROGRAMS = bluetoothd-service-audio -bluetoothd_service_headset_SOURCES = headset.c +bluetoothd_service_audio_SOURCES = manager.h manager.c headset.c -bluetoothd_service_headset_LDADD = $(top_builddir)/common/libhelper.a \ - @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ + @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ endif if ALSA @@ -30,17 +30,10 @@ libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynam libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ endif -noinst_PROGRAMS = bluetoothd-service-audio - -bluetoothd_service_audio_SOURCES = main.c manager.h manager.c - -bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ - @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ - AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS@ INCLUDES = -I$(top_srcdir)/common -EXTRA_DIST = headset.service audio-api.txt asound.conf +EXTRA_DIST = audio.service audio-api.txt asound.conf MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/audio.service b/audio/audio.service new file mode 100644 index 00000000..3db5aa32 --- /dev/null +++ b/audio/audio.service @@ -0,0 +1,4 @@ +[Bluetooth Service] +Identifier=audio +Name=Audio service +Description=Bluetooth Audio service diff --git a/audio/headset.service b/audio/headset.service deleted file mode 100644 index b2f043ee..00000000 --- a/audio/headset.service +++ /dev/null @@ -1,4 +0,0 @@ -[Bluetooth Service] -Identifier=headset -Name=Headset service -Description=Bluetooth Headset and Handsfree service -- cgit From ebfe5886d05091a0d2398182a432241435ae2956 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Apr 2007 20:08:48 +0000 Subject: Call it audio service --- audio/headset.c | 45 ++++----------------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 81d25550..fcff8174 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2005-2006 Marcel Holtmann + * Copyright (C) 2004-2007 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify @@ -1963,38 +1963,6 @@ void audio_manager_free(struct manager *manager) g_free(manager); } -static gboolean register_service(const char *ident, const char *name, - const char *desc) -{ - DBusMessage *msg, *reply; - DBusError derr; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "RegisterService"); - - if (!msg) { - error("Unable to allocate new message"); - return FALSE; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &ident, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &desc, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); - if (dbus_error_is_set(&derr)) { - error("RegisterService: %s", derr.message); - dbus_error_free(&derr); - return FALSE; - } - - dbus_message_unref(reply); - - return TRUE; -} - static void sig_term(int sig) { g_main_loop_quit(main_loop); @@ -2044,7 +2012,7 @@ int main(int argc, char *argv[]) if (optind < argc && argv[optind]) opt_bda = argv[optind]; - start_logging("headset", "Bluetooth headset service daemon"); + start_logging("audio", "Bluetooth audio service daemon"); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; @@ -2065,13 +2033,8 @@ int main(int argc, char *argv[]) exit(1); } - if (register_svc && !register_service("headset", "Headset service", - "Headset service")) { - error("Unable to register service"); - dbus_connection_unref(connection); - g_main_loop_unref(main_loop); - exit(1); - } + if (register_svc) + register_external_service(connection, "audio", "Audio service", ""); manager = audio_manager_new(connection); if (!manager) { -- cgit From 7558cca5c0bd954dc3db559c0d58ae6e497b13c0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Apr 2007 23:13:18 +0000 Subject: Add generic audio service init --- audio/Makefile.am | 6 +-- audio/headset.c | 108 ++++-------------------------------------------------- audio/headset.h | 25 +++++++++++++ audio/main.c | 62 ++++++++++++++++++++++++++++++- 4 files changed, 96 insertions(+), 105 deletions(-) create mode 100644 audio/headset.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index cbb27956..fe5931b1 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -10,10 +10,10 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio -bluetoothd_service_audio_SOURCES = manager.h manager.c headset.c +bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.c bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ - @SBC_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ + @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ endif if ALSA @@ -23,7 +23,7 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -libasound_module_pcm_bluetooth_la_LIBADD = @ALSA_LIBS@ +libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic diff --git a/audio/headset.c b/audio/headset.c index fcff8174..9e303782 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1963,78 +1963,11 @@ void audio_manager_free(struct manager *manager) g_free(manager); } -static void sig_term(int sig) -{ - g_main_loop_quit(main_loop); -} +static struct manager *manager = NULL; -int main(int argc, char *argv[]) +int headset_init(DBusConnection *conn) { - uint8_t opt_channel = 12; - char *opt_bda = NULL; - char *opt_input = NULL; - char *opt_output = NULL; - gboolean register_svc = FALSE; - bdaddr_t bda; - struct headset *hs; - struct manager *manager; - struct sigaction sa; - int opt; - - while ((opt = getopt(argc, argv, "c:o:i:ds")) != EOF) { - switch (opt) { - case 'c': - opt_channel = strtol(optarg, NULL, 0); - break; - - case 'i': - opt_input = optarg; - break; - - case 'o': - opt_output = optarg; - break; - - case 'd': - enable_debug(); - break; - - case 's': - register_svc = TRUE; - break; - - default: - printf("Usage: %s -c local_channel [-d] [-o output] [-i input] [bdaddr]\n", argv[0]); - exit(1); - } - } - - if (optind < argc && argv[optind]) - opt_bda = argv[optind]; - - start_logging("audio", "Bluetooth audio service daemon"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - main_loop = g_main_loop_new(NULL, FALSE); - - connection = init_dbus(NULL, NULL, NULL); - if (!connection) { - error("Connection to system bus failed"); - g_main_loop_unref(main_loop); - exit(1); - } - - if (register_svc) - register_external_service(connection, "audio", "Audio service", ""); + connection = dbus_connection_ref(conn); manager = audio_manager_new(connection); if (!manager) { @@ -2044,40 +1977,15 @@ int main(int argc, char *argv[]) exit(1); } - audio_manager_create_headset_server(manager, opt_channel); - - if (opt_bda) { - str2ba(opt_bda, &bda); - hs = audio_headset_new(connection, &bda); - if (!hs) { - error("Connection setup failed"); - dbus_connection_unref(connection); - g_main_loop_unref(main_loop); - exit(1); - } - - if (opt_output) - audio_headset_open_output(hs, opt_output); - if (opt_input) - audio_headset_open_input(hs, opt_input); + audio_manager_create_headset_server(manager, 12); - audio_manager_add_headset(manager, hs); - /* connect */ - hs_connect(hs, NULL); - } - - g_main_loop_run(main_loop); + return 0; +} +void headset_exit(void) +{ audio_manager_free(manager); manager = NULL; dbus_connection_unref(connection); - - g_main_loop_unref(main_loop); - - info("Exit"); - - stop_logging(); - - return 0; } diff --git a/audio/headset.h b/audio/headset.h new file mode 100644 index 00000000..a87be7e3 --- /dev/null +++ b/audio/headset.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +int headset_init(DBusConnection *conn); +void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index 2f3a1fa0..08194d82 100644 --- a/audio/main.c +++ b/audio/main.c @@ -25,16 +25,74 @@ #include #endif -#include -#include +#include +#include +#include + +#include + +#include + +#include "dbus.h" +#include "logging.h" #include "manager.h" +#include "headset.h" + +static GMainLoop *main_loop = NULL; + +static void sig_term(int sig) +{ + g_main_loop_quit(main_loop); +} int main(int argc, char *argv[]) { + DBusConnection *conn; + struct sigaction sa; + + start_logging("audio", "Bluetooth Audio daemon"); + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = sig_term; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + enable_debug(); + + main_loop = g_main_loop_new(NULL, FALSE); + + conn = dbus_bus_system_setup_with_main_loop(NULL, NULL, NULL); + if (!conn) { + g_main_loop_unref(main_loop); + exit(1); + } + audio_init(); + headset_init(conn); + + if (argc > 1 && !strcmp(argv[1], "-s")) + register_external_service(conn, "audio", "Audio service", ""); + + g_main_loop_run(main_loop); + + headset_exit(); + audio_exit(); + dbus_connection_unref(conn); + + g_main_loop_unref(main_loop); + + info("Exit"); + + stop_logging(); + return 0; } -- cgit From 353376a06677af64fc6e81d3118adc2e059516b8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Apr 2007 23:19:19 +0000 Subject: Fix some forgotton leftovers --- audio/headset.c | 7 ++++--- audio/main.c | 2 +- audio/manager.c | 11 ++++++++++- audio/manager.h | 2 +- 4 files changed, 16 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 9e303782..44a6ce03 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -112,7 +112,6 @@ struct manager { }; static DBusConnection *connection = NULL; -static GMainLoop *main_loop = NULL; struct manager *audio_manager_new(DBusConnection *conn); void audio_manager_free(struct manager *manager); @@ -1973,8 +1972,7 @@ int headset_init(DBusConnection *conn) if (!manager) { error("Failed to create an audio manager"); dbus_connection_unref(connection); - g_main_loop_unref(main_loop); - exit(1); + return -1; } audio_manager_create_headset_server(manager, 12); @@ -1985,7 +1983,10 @@ int headset_init(DBusConnection *conn) void headset_exit(void) { audio_manager_free(manager); + manager = NULL; dbus_connection_unref(connection); + + connection = NULL; } diff --git a/audio/main.c b/audio/main.c index 08194d82..bf0ddf4d 100644 --- a/audio/main.c +++ b/audio/main.c @@ -73,7 +73,7 @@ int main(int argc, char *argv[]) exit(1); } - audio_init(); + audio_init(conn); headset_init(conn); diff --git a/audio/manager.c b/audio/manager.c index 7c7a92c0..32eea81b 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -25,13 +25,22 @@ #include #endif +#include + #include "manager.h" -int audio_init(void) +static DBusConnection *connection = NULL; + +int audio_init(DBusConnection *conn) { + connection = dbus_connection_ref(conn); + return 0; } void audio_exit(void) { + dbus_connection_unref(connection); + + connection = NULL; } diff --git a/audio/manager.h b/audio/manager.h index 3f2988f9..f97b563a 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -21,5 +21,5 @@ * */ -int audio_init(void); +int audio_init(DBusConnection *conn); void audio_exit(void); -- cgit From 67d220c148f13abca61977b3036ea9f356db9205 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 9 Apr 2007 01:14:21 +0000 Subject: Simple process communication --- audio/ctl_bluetooth.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++- audio/manager.c | 83 +++++++++++++++++ 2 files changed, 325 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 511a1b24..4e55058e 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -25,14 +25,254 @@ #include #endif +#include +#include + #include #include +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) + +#define SOCKET_NAME "/org/bluez/audio" + +#define BLUETOOTH_MINVOL 0 +#define BLUETOOTH_MAXVOL 15 + +struct bluetooth_data { + snd_ctl_ext_t ext; + int sock; +}; + +enum { + BLUETOOTH_PLAYBACK, + BLUETOOTH_CAPTURE, +}; + +static const char *vol_devices[2] = { + [BLUETOOTH_PLAYBACK] = "Playback volume", + [BLUETOOTH_CAPTURE] = "Capture volume", +}; + +static void bluetooth_close(snd_ctl_ext_t *ext) +{ + struct bluetooth_data *data = ext->private_data; + + DBG("ext %p", ext); + + close(data->sock); + + free(data); +} + +static int bluetooth_elem_count(snd_ctl_ext_t *ext) +{ + DBG("ext %p", ext); + + return 2; +} + +static int bluetooth_elem_list(snd_ctl_ext_t *ext, + unsigned int offset, snd_ctl_elem_id_t *id) +{ + DBG("ext %p offset %d id %p", ext, offset, id); + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + + if (offset > 1) + return -EINVAL; + + snd_ctl_elem_id_set_name(id, vol_devices[offset]); + + return 0; +} + +static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext, + const snd_ctl_elem_id_t *id) +{ + const char *name = snd_ctl_elem_id_get_name(id); + int i; + + DBG("ext %p id %p name '%s'", ext, id, name); + + for (i = 0; i < 2; i++) + if (strcmp(name, vol_devices[i]) == 0) + return i; + + return SND_CTL_EXT_KEY_NOT_FOUND; +} + +static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, + int *type, unsigned int *acc, unsigned int *count) +{ + DBG("ext %p key %td", ext, key); + + *type = SND_CTL_ELEM_TYPE_INTEGER; + *acc = SND_CTL_EXT_ACCESS_READWRITE; + *count = 1; + + return 0; +} + +static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, + long *imin, long *imax, long *istep) +{ + DBG("ext %p key %td", ext, key); + + *istep = 1; + *imin = BLUETOOTH_MINVOL; + *imax = BLUETOOTH_MAXVOL; + + return 0; +} + +static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, + long *value) +{ + struct bluetooth_data *data = ext->private_data; + unsigned char buf[] = { 0x00, 0x00 }; + int len; + + DBG("ext %p key %td", ext, key); + + len = write(data->sock, buf, sizeof(buf)); + + *value = 0; + + return 0; +} + +static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, + long *value) +{ + struct bluetooth_data *data = ext->private_data; + unsigned char buf[] = { 0xff, 0xff }; + int len; + + DBG("ext %p key %td", ext, key); + + len = write(data->sock, buf, sizeof(buf)); + + return 0; +} + +static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, + unsigned int *event_mask) +{ + struct bluetooth_data *data = ext->private_data; + unsigned char buf[128]; + int len; + + //DBG("ext %p id %p", ext, id); + + len = recv(data->sock, buf, sizeof(buf), MSG_DONTWAIT); + + return 0; +} + +static snd_ctl_ext_callback_t bluetooth_callback = { + .close = bluetooth_close, + .elem_count = bluetooth_elem_count, + .elem_list = bluetooth_elem_list, + .find_elem = bluetooth_find_elem, + .get_attribute = bluetooth_get_attribute, + .get_integer_info = bluetooth_get_integer_info, + .read_integer = bluetooth_read_integer, + .write_integer = bluetooth_write_integer, + .read_event = bluetooth_read_event, +}; + SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) { - printf("Bluetooth control plugin\n"); + snd_config_iterator_t iter, next; + struct bluetooth_data *data; + struct sockaddr_un addr; + unsigned int id; + int sk, err; + + DBG(""); + + snd_config_for_each(iter, next, conf) { + snd_config_t *n = snd_config_iterator_entry(iter); + const char *id; + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) + continue; + + SNDERR("Unknown field %s", id); + + return -EINVAL; + } + + id = abs(getpid() * rand()); + + sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (sk < 0) { + SNDERR("Can't open socket"); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + SNDERR("Can't bind socket"); + close(sk); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + SNDERR("Can't connect socket"); + close(sk); + return -errno; + } + + data = malloc(sizeof(*data)); + if (!data) { + close(sk); + return -ENOMEM; + } + + memset(data, 0, sizeof(*data)); + + data->sock = sk; + + data->ext.version = SND_CTL_EXT_VERSION; + data->ext.card_idx = -1; + + strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1); + strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1); + strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1); + strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1); + strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1); + + data->ext.callback = &bluetooth_callback; + data->ext.poll_fd = sk; + data->ext.private_data = data; + + err = snd_ctl_ext_create(&data->ext, name, mode); + if (err < 0) + goto error; + + *handlep = data->ext.handle; + + return 0; + +error: + free(data); - return -EIO; + return err; } SND_CTL_PLUGIN_SYMBOL(bluetooth); diff --git a/audio/manager.c b/audio/manager.c index 32eea81b..a7e3afd9 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -25,14 +25,93 @@ #include #endif +#include +#include +#include +#include +#include + +#include + #include +#include "dbus.h" +#include "logging.h" + #include "manager.h" +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define SOCKET_NAME "/org/bluez/audio" + static DBusConnection *connection = NULL; +static int unix_sock = -1; + +static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct sockaddr_un addr; + socklen_t addrlen; + unsigned char buf[128]; + int sk, len; + + debug("chan %p cond %td data %p", chan, cond, data); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + g_io_channel_close(chan); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + + len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen); + + debug("path %s len %d", addr.sun_path + 1, len); + + return TRUE; +} + int audio_init(DBusConnection *conn) { + GIOChannel *io; + struct sockaddr_un addr; + int sk; + + sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (sk < 0) { + error("Can't create unix socket: %s (%d)", strerror(errno), errno); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("Can't bind unix socket: %s (%d)", strerror(errno), errno); + close(sk); + return -1; + } + + set_nonblocking(sk); + + unix_sock = sk; + + io = g_io_channel_unix_new(sk); + + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + unix_event, NULL); + + g_io_channel_unref(io); + connection = dbus_connection_ref(conn); return 0; @@ -40,6 +119,10 @@ int audio_init(DBusConnection *conn) void audio_exit(void) { + close(unix_sock); + + unix_sock = -1; + dbus_connection_unref(connection); connection = NULL; -- cgit From 35f12dee95378056a2ed7c0d325b2c3b68022da8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Apr 2007 11:44:10 +0000 Subject: Audio service refactoring: move all manager functions to manager.c --- audio/headset.c | 459 ++++++++------------------------------------------------ audio/headset.h | 38 ++++- audio/main.c | 4 - audio/manager.c | 323 ++++++++++++++++++++++++++++++++++++++- audio/manager.h | 25 +++ 5 files changed, 444 insertions(+), 405 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 44a6ce03..85c233d5 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,14 +51,11 @@ #include "dbus.h" #include "logging.h" - -#define BUF_SIZE 1024 +#include "manager.h" +#include "headset.h" #define RING_INTERVAL 3000 -#define AUDIO_MANAGER_PATH "/org/bluez/audio" -#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" - typedef enum { HEADSET_EVENT_KEYPRESS, HEADSET_EVENT_GAIN, @@ -77,7 +74,6 @@ typedef enum { struct pending_connect { int ch; - DBusConnection *conn; DBusMessage *msg; GIOChannel *io; }; @@ -102,31 +98,10 @@ struct headset { headset_state_t state; struct pending_connect *pending_connect; -}; -struct manager { - GIOChannel *server_sk; - uint32_t record_id; - int default_hs; - GSList *headset_list; + DBusConnection *conn; }; -static DBusConnection *connection = NULL; - -struct manager *audio_manager_new(DBusConnection *conn); -void audio_manager_free(struct manager *manager); -struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda); -void audio_manager_add_headset(struct manager *manager, struct headset *hs); -gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan); - -struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); -void audio_headset_unref(struct headset *hs); -gboolean audio_headset_close_input(struct headset *hs); -gboolean audio_headset_open_input(struct headset *hs, const char *audio_input); -gboolean audio_headset_close_output(struct headset *hs); -gboolean audio_headset_open_output(struct headset *hs, const char *audio_output); -GIOError audio_headset_send_ring(struct headset *hs); - static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); static void pending_connect_free(struct pending_connect *c) @@ -135,8 +110,6 @@ static void pending_connect_free(struct pending_connect *c) g_io_channel_unref(c->io); if (c->msg) dbus_message_unref(c->msg); - if (c->conn) - dbus_connection_unref(c->conn); g_free(c); } @@ -157,13 +130,6 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, return send_message_and_unref(conn, derr); } -static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr) -{ - return error_reply(conn, msg, "org.bluez.Error.InvalidArguments", - descr ? descr : "Invalid arguments in method call"); -} - static DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) { return error_reply(conn, msg, "org.bluez.Error.AlreadyConnected", @@ -226,7 +192,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(connection, signal); + send_message_and_unref(hs->conn, signal); } static void hs_signal(struct headset *hs, const char *name) @@ -239,7 +205,7 @@ static void hs_signal(struct headset *hs, const char *name) return; } - send_message_and_unref(connection, signal); + send_message_and_unref(hs->conn, signal); } static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) @@ -398,7 +364,7 @@ static void send_cancel_auth(struct headset *hs) dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - send_message_and_unref(connection, cancel); + send_message_and_unref(hs->conn, cancel); } static void auth_callback(DBusPendingCall *call, void *data) @@ -435,7 +401,7 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, struct manager *manager) { int srv_sk, cli_sk; @@ -468,16 +434,17 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; } - hs = audio_manager_find_headset_by_bda(manager, &addr.rc_bdaddr); + hs = manager_find_headset_by_bda(manager, &addr.rc_bdaddr); if (!hs) { - hs = audio_headset_new(connection, &addr.rc_bdaddr); + hs = audio_headset_new(manager_get_dbus_conn(manager), + &addr.rc_bdaddr); if (!hs) { error("Unable to create a new headset object"); close(cli_sk); return TRUE; } - audio_manager_add_headset(manager, hs); + manager_add_headset(manager, hs); } if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { @@ -505,7 +472,7 @@ static gboolean server_io_cb(GIOChannel *chan, GIOCondition cond, dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { + if (dbus_connection_send_with_reply(hs->conn, auth, &pending, -1) == FALSE) { error("Sending of authorization request failed"); goto failed; } @@ -655,7 +622,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); } /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ @@ -672,7 +639,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->pending_connect) { - err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); + err_connect_failed(hs->conn, hs->pending_connect->msg, err); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -731,7 +698,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(hs->pending_connect->conn, reply); + send_message_and_unref(hs->conn, reply); } pending_connect_free(hs->pending_connect); @@ -742,7 +709,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he failed: if (hs->pending_connect) { - err_connect_failed(hs->pending_connect->conn, hs->pending_connect->msg, err); + err_connect_failed(hs->conn, hs->pending_connect->msg, err); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -828,47 +795,11 @@ failed: return -1; } -static int server_socket(uint8_t *channel) +gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) { - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return -1; - } - - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return -1; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return -1; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; + const struct headset *hs = headset; - return sock; + return bacmp(&hs->bda, bda); } static int create_ag_record(sdp_buf_t *buf, uint8_t ch) @@ -932,7 +863,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -static uint32_t add_ag_record(uint8_t channel) +uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel) { DBusMessage *msg, *reply; DBusError derr; @@ -956,7 +887,7 @@ static uint32_t add_ag_record(uint8_t channel) &buf.data, buf.data_size, DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr); free(buf.data); dbus_message_unref(msg); @@ -984,7 +915,7 @@ static uint32_t add_ag_record(uint8_t channel) return rec_id; } -static int remove_ag_record(uint32_t rec_id) +int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id) { DBusMessage *msg, *reply; DBusError derr; @@ -1000,7 +931,7 @@ static int remove_ag_record(uint32_t rec_id) DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr); dbus_message_unref(msg); @@ -1035,7 +966,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); dbus_error_free(&derr); goto failed; } @@ -1045,14 +976,14 @@ static void get_record_reply(DBusPendingCall *call, void *data) DBUS_TYPE_INVALID)) { error("Unable to get args from GetRecordReply"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } if (!array) { error("Unable to get handle array from reply"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } @@ -1060,7 +991,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (!record) { error("Unable to extract service record from reply"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } @@ -1078,14 +1009,14 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (c->ch == -1) { error("Unable to extract RFCOMM channel from service record"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } if (rfcomm_connect(hs, &err) < 0) { error("Unable to connect"); if (c->msg) - err_connect_failed(c->conn, c->msg, err); + err_connect_failed(hs->conn, c->msg, err); goto failed; } @@ -1109,7 +1040,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) DBusMessage *reply = NULL; if (!hs || !hs->sco) - return err_not_connected(connection, msg); + return err_not_connected(hs->conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1120,8 +1051,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(hs->pending_connect->conn, - hs->pending_connect->msg, + err_connect_failed(hs->conn, hs->pending_connect->msg, EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -1131,7 +1061,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) close_sco(hs); if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1162,8 +1092,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(hs->pending_connect->conn, - hs->pending_connect->msg, + err_connect_failed(hs->conn, hs->pending_connect->msg, EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -1180,7 +1109,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) hs->data_length = 0; if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1204,7 +1133,7 @@ static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg) dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1231,9 +1160,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data) error("GetRemoteServiceHandles failed: %s", derr.message); if (c->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(c->conn, c->msg, EHOSTDOWN); + err_connect_failed(hs->conn, c->msg, EHOSTDOWN); else - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); } dbus_error_free(&derr); goto failed; @@ -1245,21 +1174,21 @@ static void get_handles_reply(DBusPendingCall *call, void *data) error("Unable to get args from reply"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } if (!array) { error("Unable to get handle array from reply"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); if (c->msg) - err_not_supported(c->conn, c->msg); + err_not_supported(hs->conn, c->msg); goto failed; } @@ -1272,7 +1201,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (!msg) { error("Unable to allocate new method call"); if (c->msg) - err_connect_failed(c->conn, c->msg, ENOMEM); + err_connect_failed(hs->conn, c->msg, ENOMEM); goto failed; } @@ -1284,10 +1213,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); if (c->msg) - err_connect_failed(c->conn, c->msg, EIO); + err_connect_failed(hs->conn, c->msg, EIO); goto failed; } @@ -1327,7 +1256,6 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; - hs->pending_connect->conn = dbus_connection_ref(connection); hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL; msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", @@ -1347,13 +1275,13 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; dbus_message_unref(msg); - return err_connect_failed(connection, msg, EIO); + return err_connect_failed(hs->conn, msg, EIO); } dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); @@ -1363,7 +1291,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED;; } -GIOError audio_headset_send_ring(struct headset *hs) +static GIOError audio_headset_send_ring(struct headset *hs) { const char *ring_str = "\r\nRING\r\n"; GIOError err; @@ -1408,7 +1336,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); + return err_not_connected(hs->conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1423,14 +1351,14 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(connection, msg); + return err_failed(hs->conn, msg); } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); done: if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1439,8 +1367,8 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) { DBusMessage *reply = NULL; - if (!hs) - return err_not_connected(connection, msg); + if (hs->state < HEADSET_STATE_CONNECTED) + return err_not_connected(hs->conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1458,7 +1386,7 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) done: if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(hs->conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1469,17 +1397,14 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) struct pending_connect *c; int sk, err; - if (!hs) - return err_not_connected(connection, msg); - if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); /* FIXME: in progress error? */ + return err_not_connected(hs->conn, msg); /* FIXME: in progress error? */ if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) - return err_already_connected(connection, msg); + return err_already_connected(hs->conn, msg); if (hs->sco) - return err_already_connected(connection, msg); + return err_already_connected(hs->conn, msg); c = g_try_new0(struct pending_connect, 1); if (!c) @@ -1487,14 +1412,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - c->conn = dbus_connection_ref(connection); c->msg = msg ? dbus_message_ref(msg) : NULL; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(connection, msg, err); + err_connect_failed(hs->conn, msg, err); goto failed; } @@ -1512,13 +1436,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(connection, msg, err); + err_connect_failed(hs->conn, msg, err); goto failed; } if (set_nonblocking(sk) < 0) { err = errno; - err_connect_failed(connection, msg, err); + err_connect_failed(hs->conn, msg, err); goto failed; } @@ -1626,6 +1550,8 @@ struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) bacpy(&hs->bda, bda); + hs->conn = dbus_connection_ref(conn); + return hs; } @@ -1633,6 +1559,8 @@ void audio_headset_unref(struct headset *hs) { assert(hs != NULL); + dbus_connection_unref(hs->conn); + g_free(hs); } @@ -1727,266 +1655,7 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input) return TRUE; } -gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) -{ - int srv_sk; - - assert(manager != NULL); - - if (manager->server_sk) { - error("Server socket already created"); - return FALSE; - } - - srv_sk = server_socket(&chan); - if (srv_sk < 0) { - error("Unable to create server socket"); - return FALSE; - } - - if (!manager->record_id) - manager->record_id = add_ag_record(chan); - - if (!manager->record_id) { - error("Unable to register service record"); - close(srv_sk); - return FALSE; - } - - manager->server_sk = g_io_channel_unix_new(srv_sk); - if (!manager->server_sk) { - error("Unable to allocate new GIOChannel"); - remove_ag_record(manager->record_id); - manager->record_id = 0; - close(srv_sk); - return FALSE; - } - - g_io_add_watch(manager->server_sk, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) server_io_cb, manager); - - return TRUE; -} - -static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) -{ - const struct headset *hs = headset; - - return bacmp(&hs->bda, bda); -} - -struct headset *audio_manager_find_headset_by_bda(struct manager *manager, const bdaddr_t *bda) -{ - GSList *elem; - - assert(manager); - elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); - - return elem ? elem->data : NULL; -} - -void audio_manager_add_headset(struct manager *manager, struct headset *hs) -{ - assert(manager && hs); - - if (g_slist_find(manager->headset_list, hs)) - return; - - manager->headset_list = g_slist_append(manager->headset_list, hs); -} - -static DBusHandlerResult am_create_headset(struct manager *manager, - DBusMessage *msg) +const char *audio_headset_get_path(struct headset *hs) { - const char *object_path; - const char *address; - struct headset *hs; - bdaddr_t bda; - DBusMessage *reply; - DBusError derr; - - if (!manager) - return err_not_connected(connection, msg); - - dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - err_invalid_args(connection, msg, derr.message); - return DBUS_HANDLER_RESULT_HANDLED; - } - if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - str2ba(address, &bda); - hs = audio_manager_find_headset_by_bda(manager, &bda); - if (!hs) { - hs = audio_headset_new(connection, &bda); - if (!hs) - return error_reply(connection, msg, - "org.bluez.Error.Failed", - "Unable to create new headset object"); - audio_manager_add_headset(manager, hs); - } - - object_path = hs->object_path; - dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, - DBUS_TYPE_INVALID); - - return send_message_and_unref(connection, reply); -} - -static DBusHandlerResult am_remove_headset(struct manager *manager, - DBusMessage *msg) -{ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult am_list_headsets(struct manager *manager, - DBusMessage *msg) -{ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult am_get_default_headset(struct manager *manager, - DBusMessage *msg) -{ - DBusMessage *reply; - char object_path[128]; - const char *opath = object_path; - - if (!manager) - return err_not_connected(connection, msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", - manager->default_hs); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, - DBUS_TYPE_INVALID); - - return send_message_and_unref(connection, reply); -} - -static DBusHandlerResult am_change_default_headset(struct manager *manager, - DBusMessage *msg) -{ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static DBusHandlerResult am_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *interface, *member; - struct manager *manager = data; - - interface = dbus_message_get_interface(msg); - member = dbus_message_get_member(msg); - - if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && - !strcmp("Introspect", member)) - return simple_introspect(conn, msg, data); - - if (strcmp(interface, "org.bluez.audio.Manager") != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(member, "CreateHeadset") == 0) - return am_create_headset(manager, msg); - - if (strcmp(member, "RemoveHeadset") == 0) - return am_remove_headset(manager, msg); - - if (strcmp(member, "ListHeadsets") == 0) - return am_list_headsets(manager, msg); - - if (strcmp(member, "DefaultHeadset") == 0) - return am_get_default_headset(manager, msg); - - if (strcmp(member, "ChangeDefaultHeadset") == 0) - return am_change_default_headset(manager, msg); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static const DBusObjectPathVTable am_table = { - .message_function = am_message, -}; - -struct manager *audio_manager_new(DBusConnection *conn) -{ - struct manager *manager; - - manager = g_new0(struct manager, 1); - - if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, - &am_table, manager)) { - error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - g_free(manager); - return NULL; - } - - return manager; -} - -void audio_manager_free(struct manager *manager) -{ - assert(manager != NULL); - - if (manager->record_id) { - remove_ag_record(manager->record_id); - manager->record_id = 0; - } - - if (manager->server_sk) { - g_io_channel_unref(manager->server_sk); - manager->server_sk = NULL; - } - - if (manager->headset_list) { - g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref, - manager); - g_slist_free(manager->headset_list); - manager->headset_list = NULL; - } - - g_free(manager); -} - -static struct manager *manager = NULL; - -int headset_init(DBusConnection *conn) -{ - connection = dbus_connection_ref(conn); - - manager = audio_manager_new(connection); - if (!manager) { - error("Failed to create an audio manager"); - dbus_connection_unref(connection); - return -1; - } - - audio_manager_create_headset_server(manager, 12); - - return 0; -} - -void headset_exit(void) -{ - audio_manager_free(manager); - - manager = NULL; - - dbus_connection_unref(connection); - - connection = NULL; + return hs->object_path; } diff --git a/audio/headset.h b/audio/headset.h index a87be7e3..3027a9c1 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,6 +20,40 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AUDIO_HEADSET_H +#define __AUDIO_HEADSET_H -int headset_init(DBusConnection *conn); -void headset_exit(void); +#include + +#include + +struct headset; + +#include "manager.h" + +#define BUF_SIZE 1024 + +struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); + +void audio_headset_unref(struct headset *hs); + +uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel); + +int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id); + +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, + struct manager *manager); + +gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); + +const char *audio_headset_get_path(struct headset *hs); + +gboolean audio_headset_close_output(struct headset *hs); + +gboolean audio_headset_open_output(struct headset *hs, const char *output); + +gboolean audio_headset_close_input(struct headset *hs); + +gboolean audio_headset_open_input(struct headset *hs, const char *input); + +#endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/main.c b/audio/main.c index bf0ddf4d..6ef3b1c6 100644 --- a/audio/main.c +++ b/audio/main.c @@ -75,15 +75,11 @@ int main(int argc, char *argv[]) audio_init(conn); - headset_init(conn); - if (argc > 1 && !strcmp(argv[1], "-s")) register_external_service(conn, "audio", "Audio service", ""); g_main_loop_run(main_loop); - headset_exit(); - audio_exit(); dbus_connection_unref(conn); diff --git a/audio/manager.c b/audio/manager.c index a7e3afd9..743e50d5 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -28,9 +28,14 @@ #include #include #include +#include +#include #include #include +#include +#include + #include #include @@ -38,6 +43,7 @@ #include "dbus.h" #include "logging.h" +#include "headset.h" #include "manager.h" #ifndef UNIX_PATH_MAX @@ -46,10 +52,43 @@ #define SOCKET_NAME "/org/bluez/audio" -static DBusConnection *connection = NULL; +struct manager { + DBusConnection *conn; + GIOChannel *hs_server; + uint32_t hs_record_id; + int default_hs; + GSList *headset_list; +}; + +static struct manager *manager = NULL; static int unix_sock = -1; +/* FIXME: Remove these once global error functions exist */ +static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr) +{ + DBusMessage *derr; + + if (!conn || !msg) + return DBUS_HANDLER_RESULT_HANDLED; + + derr = dbus_message_new_error(msg, name, descr); + if (!derr) { + error("Unable to allocate new error return"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + return send_message_and_unref(conn, derr); +} + +static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr) +{ + return error_reply(conn, msg, "org.bluez.Error.InvalidArguments", + descr ? descr : "Invalid arguments in method call"); +} + static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; @@ -79,6 +118,276 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } +static GIOChannel *server_socket(uint8_t *channel) +{ + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +static gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) +{ + assert(manager != NULL); + + if (manager->hs_server) { + error("Server socket already created"); + return FALSE; + } + + manager->hs_server = server_socket(&chan); + if (!manager->hs_server) + return FALSE; + + if (!manager->hs_record_id) + manager->hs_record_id = headset_add_ag_record(manager->conn, chan); + + if (!manager->hs_record_id) { + error("Unable to register service record"); + g_io_channel_unref(manager->hs_server); + manager->hs_server = NULL; + return FALSE; + } + + g_io_add_watch(manager->hs_server, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) headset_server_io_cb, manager); + + return TRUE; +} + +struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *bda) +{ + GSList *elem; + + assert(manager); + elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); + + return elem ? elem->data : NULL; +} + +void manager_add_headset(struct manager *manager, struct headset *hs) +{ + assert(manager && hs); + + if (g_slist_find(manager->headset_list, hs)) + return; + + manager->headset_list = g_slist_append(manager->headset_list, hs); +} + +static DBusHandlerResult am_create_headset(struct manager *manager, + DBusMessage *msg) +{ + const char *object_path; + const char *address; + struct headset *hs; + bdaddr_t bda; + DBusMessage *reply; + DBusError derr; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID)) { + err_invalid_args(manager->conn, msg, derr.message); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_error_is_set(&derr)) { + err_invalid_args(manager->conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + str2ba(address, &bda); + hs = manager_find_headset_by_bda(manager, &bda); + if (!hs) { + hs = audio_headset_new(manager->conn, &bda); + if (!hs) + return error_reply(manager->conn, msg, + "org.bluez.Error.Failed", + "Unable to create new headset object"); + manager_add_headset(manager, hs); + } + + object_path = audio_headset_get_path(hs); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, + DBUS_TYPE_INVALID); + + return send_message_and_unref(manager->conn, reply); +} + +static DBusHandlerResult am_remove_headset(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_list_headsets(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_get_default_headset(struct manager *manager, + DBusMessage *msg) +{ + DBusMessage *reply; + char object_path[128]; + const char *opath = object_path; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", + manager->default_hs); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, + DBUS_TYPE_INVALID); + + return send_message_and_unref(manager->conn, reply); +} + +static DBusHandlerResult am_change_default_headset(struct manager *manager, + DBusMessage *msg) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_message(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + const char *interface, *member; + struct manager *manager = data; + + interface = dbus_message_get_interface(msg); + member = dbus_message_get_member(msg); + + if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && + !strcmp("Introspect", member)) + return simple_introspect(conn, msg, data); + + if (strcmp(interface, "org.bluez.audio.Manager") != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (strcmp(member, "CreateHeadset") == 0) + return am_create_headset(manager, msg); + + if (strcmp(member, "RemoveHeadset") == 0) + return am_remove_headset(manager, msg); + + if (strcmp(member, "ListHeadsets") == 0) + return am_list_headsets(manager, msg); + + if (strcmp(member, "DefaultHeadset") == 0) + return am_get_default_headset(manager, msg); + + if (strcmp(member, "ChangeDefaultHeadset") == 0) + return am_change_default_headset(manager, msg); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const DBusObjectPathVTable am_table = { + .message_function = am_message, +}; + +static struct manager *audio_manager_new(DBusConnection *conn) +{ + struct manager *manager; + + manager = g_new0(struct manager, 1); + + if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, + &am_table, manager)) { + error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); + g_free(manager); + return NULL; + } + + manager->conn = dbus_connection_ref(conn); + + return manager; +} + +static void audio_manager_free(struct manager *manager) +{ + assert(manager != NULL); + + if (manager->hs_record_id) { + headset_remove_ag_record(manager->conn, manager->hs_record_id); + manager->hs_record_id = 0; + } + + if (manager->hs_server) { + g_io_channel_unref(manager->hs_server); + manager->hs_server = NULL; + } + + if (manager->headset_list) { + g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref, + manager); + g_slist_free(manager->headset_list); + manager->headset_list = NULL; + } + + dbus_connection_unref(manager->conn); + + g_free(manager); +} + +DBusConnection *manager_get_dbus_conn(struct manager *manager) +{ + return manager->conn; +} + int audio_init(DBusConnection *conn) { GIOChannel *io; @@ -112,7 +421,13 @@ int audio_init(DBusConnection *conn) g_io_channel_unref(io); - connection = dbus_connection_ref(conn); + manager = audio_manager_new(conn); + if (!manager) { + error("Failed to create an audio manager"); + return -1; + } + + audio_manager_create_headset_server(manager, 12); return 0; } @@ -123,7 +438,7 @@ void audio_exit(void) unix_sock = -1; - dbus_connection_unref(connection); + audio_manager_free(manager); - connection = NULL; + manager = NULL; } diff --git a/audio/manager.h b/audio/manager.h index f97b563a..8335c79a 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -20,6 +20,31 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AUDIO_MANAGER_H +#define __AUDIO_MANAGER_H + +#include + +#include + +struct manager; + +#include "headset.h" + +#define AUDIO_MANAGER_PATH "/org/bluez/audio" + +#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" + +void manager_add_headset(struct manager *manager, struct headset *hs); + +struct headset *manager_find_headset_by_bda(struct manager *manager, + bdaddr_t *bda); + +DBusConnection *manager_get_dbus_conn(struct manager *manager); int audio_init(DBusConnection *conn); + void audio_exit(void); + +#endif /* __AUDIO_MANAGER_H_ */ + -- cgit From 2a209d402e7cc324d03fb7172ac892626068d00b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Apr 2007 12:23:44 +0000 Subject: Rename functions for consistency --- audio/headset.c | 32 ++++++++++++++++---------------- audio/headset.h | 14 +++++++------- audio/manager.c | 20 ++++++++++---------- audio/manager.h | 2 +- 4 files changed, 34 insertions(+), 34 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 85c233d5..e1122bd6 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -240,7 +240,7 @@ static void close_sco(struct headset *hs) hs->audio_output = NULL; } if (hs->audio_input) - audio_headset_close_input(hs); + headset_close_input(hs); assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; hs_signal(hs, "Stopped"); @@ -436,7 +436,7 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, hs = manager_find_headset_by_bda(manager, &addr.rc_bdaddr); if (!hs) { - hs = audio_headset_new(manager_get_dbus_conn(manager), + hs = headset_new(manager_get_dbus_conn(manager), &addr.rc_bdaddr); if (!hs) { error("Unable to create a new headset object"); @@ -526,7 +526,7 @@ static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpoin return TRUE; failed: - audio_headset_close_input(hs); + headset_close_input(hs); return FALSE; } @@ -545,7 +545,7 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond goto disconn; if (!hs->audio_output && hs->output) - audio_headset_open_output(hs, hs->output); + headset_open_output(hs, hs->output); err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); @@ -1291,7 +1291,7 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED;; } -static GIOError audio_headset_send_ring(struct headset *hs) +static GIOError headset_send_ring(struct headset *hs) { const char *ring_str = "\r\nRING\r\n"; GIOError err; @@ -1323,7 +1323,7 @@ static gboolean ring_timer_cb(gpointer data) assert(hs != NULL); - if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) + if (headset_send_ring(hs) != G_IO_ERROR_NONE) error("Sending RING failed"); return TRUE; @@ -1349,7 +1349,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) goto done; } - if (audio_headset_send_ring(hs) != G_IO_ERROR_NONE) { + if (headset_send_ring(hs) != G_IO_ERROR_NONE) { dbus_message_unref(reply); return err_failed(hs->conn, msg); } @@ -1527,7 +1527,7 @@ static const DBusObjectPathVTable hs_table = { ** Create a unique dbus object path for the headset and allocates a new ** headset or return NULL if fail */ -struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) +struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda) { static int headset_uid = 0; struct headset *hs; @@ -1539,7 +1539,7 @@ struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) } snprintf(hs->object_path, sizeof(hs->object_path), - AUDIO_HEADSET_PATH_BASE "%d", headset_uid++); + HEADSET_PATH_BASE "%d", headset_uid++); if (!dbus_connection_register_object_path(conn, hs->object_path, &hs_table, hs)) { @@ -1555,7 +1555,7 @@ struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda) return hs; } -void audio_headset_unref(struct headset *hs) +void headset_unref(struct headset *hs) { assert(hs != NULL); @@ -1564,7 +1564,7 @@ void audio_headset_unref(struct headset *hs) g_free(hs); } -gboolean audio_headset_close_output(struct headset *hs) +gboolean headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -1579,13 +1579,13 @@ gboolean audio_headset_close_output(struct headset *hs) } /* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ -gboolean audio_headset_open_output(struct headset *hs, const char *output) +gboolean headset_open_output(struct headset *hs, const char *output) { int out; assert(hs != NULL && output != NULL); - audio_headset_close_output(hs); + headset_close_output(hs); if (output && hs->output) { g_free(hs->output); hs->output = g_strdup(output); @@ -1611,7 +1611,7 @@ gboolean audio_headset_open_output(struct headset *hs, const char *output) return TRUE; } -gboolean audio_headset_close_input(struct headset *hs) +gboolean headset_close_input(struct headset *hs) { assert(hs != NULL); @@ -1625,7 +1625,7 @@ gboolean audio_headset_close_input(struct headset *hs) return TRUE; } -gboolean audio_headset_open_input(struct headset *hs, const char *input) +gboolean headset_open_input(struct headset *hs, const char *input) { int in; @@ -1655,7 +1655,7 @@ gboolean audio_headset_open_input(struct headset *hs, const char *input) return TRUE; } -const char *audio_headset_get_path(struct headset *hs) +const char *headset_get_path(struct headset *hs) { return hs->object_path; } diff --git a/audio/headset.h b/audio/headset.h index 3027a9c1..9f1c1232 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -33,9 +33,9 @@ struct headset; #define BUF_SIZE 1024 -struct headset *audio_headset_new(DBusConnection *conn, const bdaddr_t *bda); +struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda); -void audio_headset_unref(struct headset *hs); +void headset_unref(struct headset *hs); uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel); @@ -46,14 +46,14 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); -const char *audio_headset_get_path(struct headset *hs); +const char *headset_get_path(struct headset *hs); -gboolean audio_headset_close_output(struct headset *hs); +gboolean headset_close_output(struct headset *hs); -gboolean audio_headset_open_output(struct headset *hs, const char *output); +gboolean headset_open_output(struct headset *hs, const char *output); -gboolean audio_headset_close_input(struct headset *hs); +gboolean headset_close_input(struct headset *hs); -gboolean audio_headset_open_input(struct headset *hs, const char *input); +gboolean headset_open_input(struct headset *hs, const char *input); #endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/manager.c b/audio/manager.c index 743e50d5..28ae615e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -169,7 +169,7 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -static gboolean audio_manager_create_headset_server(struct manager *manager, uint8_t chan) +static gboolean manager_create_headset_server(struct manager *manager, uint8_t chan) { assert(manager != NULL); @@ -249,7 +249,7 @@ static DBusHandlerResult am_create_headset(struct manager *manager, str2ba(address, &bda); hs = manager_find_headset_by_bda(manager, &bda); if (!hs) { - hs = audio_headset_new(manager->conn, &bda); + hs = headset_new(manager->conn, &bda); if (!hs) return error_reply(manager->conn, msg, "org.bluez.Error.Failed", @@ -257,7 +257,7 @@ static DBusHandlerResult am_create_headset(struct manager *manager, manager_add_headset(manager, hs); } - object_path = audio_headset_get_path(hs); + object_path = headset_get_path(hs); dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, DBUS_TYPE_INVALID); @@ -287,7 +287,7 @@ static DBusHandlerResult am_get_default_headset(struct manager *manager, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - snprintf(object_path, sizeof(object_path), AUDIO_HEADSET_PATH_BASE "%d", + snprintf(object_path, sizeof(object_path), HEADSET_PATH_BASE "%d", manager->default_hs); dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); @@ -339,7 +339,7 @@ static const DBusObjectPathVTable am_table = { .message_function = am_message, }; -static struct manager *audio_manager_new(DBusConnection *conn) +static struct manager *manager_new(DBusConnection *conn) { struct manager *manager; @@ -357,7 +357,7 @@ static struct manager *audio_manager_new(DBusConnection *conn) return manager; } -static void audio_manager_free(struct manager *manager) +static void manager_free(struct manager *manager) { assert(manager != NULL); @@ -372,7 +372,7 @@ static void audio_manager_free(struct manager *manager) } if (manager->headset_list) { - g_slist_foreach(manager->headset_list, (GFunc) audio_headset_unref, + g_slist_foreach(manager->headset_list, (GFunc) headset_unref, manager); g_slist_free(manager->headset_list); manager->headset_list = NULL; @@ -421,13 +421,13 @@ int audio_init(DBusConnection *conn) g_io_channel_unref(io); - manager = audio_manager_new(conn); + manager = manager_new(conn); if (!manager) { error("Failed to create an audio manager"); return -1; } - audio_manager_create_headset_server(manager, 12); + manager_create_headset_server(manager, 12); return 0; } @@ -438,7 +438,7 @@ void audio_exit(void) unix_sock = -1; - audio_manager_free(manager); + manager_free(manager); manager = NULL; } diff --git a/audio/manager.h b/audio/manager.h index 8335c79a..ca4df09d 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -33,7 +33,7 @@ struct manager; #define AUDIO_MANAGER_PATH "/org/bluez/audio" -#define AUDIO_HEADSET_PATH_BASE "/org/bluez/audio/headset" +#define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" void manager_add_headset(struct manager *manager, struct headset *hs); -- cgit From b8a407aa8470ad8d92d9142edb41c17548b0cb2c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 10 Apr 2007 21:37:24 +0000 Subject: Add first step of ALSA plugin integration --- audio/Makefile.am | 6 +- audio/ctl_bluetooth.c | 4 +- audio/ipc.h | 43 ++++++++++ audio/manager.c | 1 + audio/pcm_bluetooth.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 audio/ipc.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index fe5931b1..6bf33050 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -10,7 +10,7 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio -bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.c +bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.c ipc.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ @@ -21,11 +21,11 @@ alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la -libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c +libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ -libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c +libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ endif diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 4e55058e..037cfb09 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -193,7 +193,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) unsigned int id; int sk, err; - DBG(""); + DBG("Bluetooth Control plugin"); snd_config_for_each(iter, next, conf) { snd_config_t *n = snd_config_iterator_entry(iter); @@ -270,6 +270,8 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) return 0; error: + close(sk); + free(data); return err; diff --git a/audio/ipc.h b/audio/ipc.h new file mode 100644 index 00000000..5b545844 --- /dev/null +++ b/audio/ipc.h @@ -0,0 +1,43 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#include + +#define IPC_TYPE_CONNECT 0x0001 + +struct ipc_hdr { + uint16_t id; + uint16_t type; + uint16_t seqnum; + uint16_t length; +} __attribute__ ((packed)); + +struct ipc_connect_cmd { + uint8_t src[6]; + uint8_t dst[6]; + uint16_t uuid; +} __attribute__ ((packed)); + +struct ipc_connect_evt { + uint16_t id; +} __attribute__ ((packed)); diff --git a/audio/manager.c b/audio/manager.c index 28ae615e..e48d7a7a 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -43,6 +43,7 @@ #include "dbus.h" #include "logging.h" +#include "ipc.h" #include "headset.h" #include "manager.h" diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b9f4f02c..d3e457ae 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -25,14 +25,237 @@ #include #endif +#include +#include + #include #include +#include "ipc.h" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) + +#define SOCKET_NAME "/org/bluez/audio" + +struct bluetooth_data { + snd_pcm_ioplug_t io; + snd_pcm_sframes_t hw_ptr; + int sock; +}; + +static int bluetooth_start(snd_pcm_ioplug_t *io) +{ + DBG("io %p", io); + + return 0; +} + +static int bluetooth_stop(snd_pcm_ioplug_t *io) +{ + DBG("io %p", io); + + return 0; +} + +static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + + //DBG("io %p", io); + + //DBG("hw_ptr=%lu", data->hw_ptr); + + return data->hw_ptr; +} + +static int bluetooth_close(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + + DBG("io %p", io); + + free(data); + + return 0; +} + +static snd_pcm_ioplug_callback_t bluetooth_playback_callback = { + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, +#if 0 + .hw_params = bluetooth_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_write, +#endif +}; + +static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, +#if 0 + .hw_params = bluetooth_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_read, +#endif +}; + +#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) + +static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) +{ + snd_pcm_access_t access_list[] = { + SND_PCM_ACCESS_RW_INTERLEAVED, + /* Mmap access is really useless fo this driver, but we + * support it because some pieces of software out there + * insist on using it */ + SND_PCM_ACCESS_MMAP_INTERLEAVED + }; + unsigned int format_list[] = { + SND_PCM_FORMAT_S16_LE + }; + int err; + + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, + ARRAY_NELEMS(access_list), access_list); + if (err < 0) + return err; + + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, + ARRAY_NELEMS(format_list), format_list); + if (err < 0) + return err; + + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, 1, 1); + if (err < 0) + return err; + + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 8000); + if (err < 0) + return err; + + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 48, 48); + if (err < 0) + return err; + + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 200); + if (err < 0) + return err; + + return 0; +} + SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) { - printf("Bluetooth PCM plugin\n"); + snd_config_iterator_t iter, next; + struct bluetooth_data *data; + struct sockaddr_un addr; + unsigned int id; + int sk, err; + + DBG("Bluetooth PCM plugin (%s)", + stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); + + snd_config_for_each(iter, next, conf) { + snd_config_t *n = snd_config_iterator_entry(iter); + const char *id; + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) + continue; + + if (strcmp(id, "bdaddr") == 0) { + const char *str; + if (snd_config_get_string(n, &str) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + printf("bdaddr %s\n", str); + continue; + } + + SNDERR("Unknown field %s", id); + + return -EINVAL; + } + + id = abs(getpid() * rand()); + + sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (sk < 0) { + SNDERR("Can't open socket"); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + SNDERR("Can't bind socket"); + close(sk); + return -errno; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + SNDERR("Can't connect socket"); + close(sk); + return -errno; + } + + data = malloc(sizeof(*data)); + if (!data) { + close(sk); + return -ENOMEM; + } + + memset(data, 0, sizeof(*data)); + + data->sock = sk; + + data->io.version = SND_PCM_IOPLUG_VERSION; + data->io.name = "Bluetooth Audio"; + data->io.mmap_rw = 0; /* No direct mmap communication */ + + data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? + &bluetooth_playback_callback : &bluetooth_capture_callback; + data->io.poll_fd = sk; + data->io.poll_events = POLLIN; + data->io.private_data = data; + + err = snd_pcm_ioplug_create(&data->io, name, stream, mode); + if (err < 0) + goto error; + + err = bluetooth_hw_constraint(&data->io); + if (err < 0) { + snd_pcm_ioplug_delete(&data->io); + goto error; + } + + *pcmp = data->io.pcm; + + return 0; + +error: + close(sk); + + free(data); - return -EIO; + return err; } SND_PCM_PLUGIN_SYMBOL(bluetooth); -- cgit From 993e7c747c360fb05b98e316b7ec760d2fb70365 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 11 Apr 2007 08:56:50 +0000 Subject: Implement ChangeDefaultHeadset and RemoveHeadset methods --- audio/headset.c | 13 ++++++++ audio/headset.h | 1 + audio/manager.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 96 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index e1122bd6..ab4c4d8f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -795,6 +795,13 @@ failed: return -1; } +gint headset_path_cmp(gconstpointer headset, gconstpointer path) +{ + const struct headset *hs = headset; + + return strcmp(hs->object_path, path); +} + gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) { const struct headset *hs = headset; @@ -1559,6 +1566,12 @@ void headset_unref(struct headset *hs) { assert(hs != NULL); + if (hs->state > HEADSET_STATE_DISCONNECTED) + hs_disconnect(hs, NULL); + + if (!dbus_connection_unregister_object_path(hs->conn, hs->object_path)) + error("D-Bus failed to unregister %s path", hs->object_path); + dbus_connection_unref(hs->conn); g_free(hs); diff --git a/audio/headset.h b/audio/headset.h index 9f1c1232..7c85b9bb 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -44,6 +44,7 @@ int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id); gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, struct manager *manager); +gint headset_path_cmp(gconstpointer headset, gconstpointer path); gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); const char *headset_get_path(struct headset *hs); diff --git a/audio/manager.c b/audio/manager.c index e48d7a7a..2ce50cab 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -57,7 +57,7 @@ struct manager { DBusConnection *conn; GIOChannel *hs_server; uint32_t hs_record_id; - int default_hs; + struct headset *default_hs; GSList *headset_list; }; @@ -205,6 +205,7 @@ struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *b GSList *elem; assert(manager); + elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); return elem ? elem->data : NULL; @@ -212,12 +213,13 @@ struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *b void manager_add_headset(struct manager *manager, struct headset *hs) { - assert(manager && hs); - - if (g_slist_find(manager->headset_list, hs)) - return; + assert(manager); + assert(hs); manager->headset_list = g_slist_append(manager->headset_list, hs); + + if (!manager->default_hs) + manager->default_hs = hs; } static DBusHandlerResult am_create_headset(struct manager *manager, @@ -268,7 +270,44 @@ static DBusHandlerResult am_create_headset(struct manager *manager, static DBusHandlerResult am_remove_headset(struct manager *manager, DBusMessage *msg) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + DBusError derr; + DBusMessage *reply; + GSList *match; + const char *path; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + err_invalid_args(manager->conn, msg, derr.message); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_error_is_set(&derr)) { + err_invalid_args(manager->conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + match = g_slist_find_custom(manager->headset_list, path, headset_path_cmp); + if (!match) + return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + "The headset does not exist"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + headset_unref(match->data); + manager->headset_list = g_slist_remove(manager->headset_list, match->data); + + if (manager->default_hs == match->data) { + if (!manager->headset_list) + manager->default_hs = NULL; + else + manager->default_hs = manager->headset_list->data; + } + + return send_message_and_unref(manager->conn, reply); } static DBusHandlerResult am_list_headsets(struct manager *manager, @@ -281,15 +320,18 @@ static DBusHandlerResult am_get_default_headset(struct manager *manager, DBusMessage *msg) { DBusMessage *reply; - char object_path[128]; - const char *opath = object_path; + const char *opath; + + if (!manager->default_hs) + return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + "There is no default headset"); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - snprintf(object_path, sizeof(object_path), HEADSET_PATH_BASE "%d", - manager->default_hs); + opath = headset_get_path(manager->default_hs); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); @@ -299,7 +341,36 @@ static DBusHandlerResult am_get_default_headset(struct manager *manager, static DBusHandlerResult am_change_default_headset(struct manager *manager, DBusMessage *msg) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + DBusError derr; + DBusMessage *reply; + GSList *match; + const char *path; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID)) { + err_invalid_args(manager->conn, msg, derr.message); + return DBUS_HANDLER_RESULT_HANDLED; + } + if (dbus_error_is_set(&derr)) { + err_invalid_args(manager->conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + match = g_slist_find_custom(manager->headset_list, path, headset_path_cmp); + if (!match) + return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + "The headset does not exist"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + manager->default_hs = match->data; + + return send_message_and_unref(manager->conn, reply); } static DBusHandlerResult am_message(DBusConnection *conn, -- cgit From 0e64a016b580c0e51a393c682a50bc0c37c5604f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 11 Apr 2007 10:03:09 +0000 Subject: Implement ListHeadsets method --- audio/manager.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 2ce50cab..9940709e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -313,7 +313,31 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, static DBusHandlerResult am_list_headsets(struct manager *manager, DBusMessage *msg) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessage *reply; + GSList *l; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + for (l = manager->headset_list; l != NULL; l = l->next) { + struct headset *hs = l->data; + const char *path = headset_get_path(hs); + + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &path); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(manager->conn, reply); } static DBusHandlerResult am_get_default_headset(struct manager *manager, -- cgit From e0a2f55119eae4b8670a748205525ecff9bda5ae Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 11 Apr 2007 11:19:58 +0000 Subject: Cleanup, minor fixes, and missing signals --- audio/manager.c | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 9940709e..d2f7a015 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -55,10 +55,12 @@ struct manager { DBusConnection *conn; + + /* Headset specific variables */ GIOChannel *hs_server; uint32_t hs_record_id; struct headset *default_hs; - GSList *headset_list; + GSList *headsets; }; static struct manager *manager = NULL; @@ -90,6 +92,25 @@ static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg descr ? descr : "Invalid arguments in method call"); } +static void manager_signal(DBusConnection *conn, const char *name, + const char *param) +{ + DBusMessage *signal; + + signal = dbus_message_new_signal("/org/bluez/audio", + "org.bluez.audio.Manager", + name); + if (!signal) { + error("Unable to create new D-Bus signal"); + return; + } + + dbus_message_append_args(signal, DBUS_TYPE_STRING, ¶m, + DBUS_TYPE_INVALID); + + send_message_and_unref(conn, signal); +} + static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; @@ -206,7 +227,7 @@ struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *b assert(manager); - elem = g_slist_find_custom(manager->headset_list, bda, headset_bda_cmp); + elem = g_slist_find_custom(manager->headsets, bda, headset_bda_cmp); return elem ? elem->data : NULL; } @@ -216,10 +237,15 @@ void manager_add_headset(struct manager *manager, struct headset *hs) assert(manager); assert(hs); - manager->headset_list = g_slist_append(manager->headset_list, hs); + manager->headsets = g_slist_append(manager->headsets, hs); - if (!manager->default_hs) + manager_signal(manager->conn, "HeadsetCreated", headset_get_path(hs)); + + if (!manager->default_hs) { manager->default_hs = hs; + manager_signal(manager->conn, "DefaultHeadsetChanged", + headset_get_path(hs)); + } } static DBusHandlerResult am_create_headset(struct manager *manager, @@ -273,6 +299,7 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, DBusError derr; DBusMessage *reply; GSList *match; + struct headset *hs; const char *path; dbus_error_init(&derr); @@ -288,7 +315,7 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(manager->headset_list, path, headset_path_cmp); + match = g_slist_find_custom(manager->headsets, path, headset_path_cmp); if (!match) return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); @@ -297,16 +324,24 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - headset_unref(match->data); - manager->headset_list = g_slist_remove(manager->headset_list, match->data); + hs = match->data; + + manager->headsets = g_slist_remove(manager->headsets, hs); - if (manager->default_hs == match->data) { - if (!manager->headset_list) + if (manager->default_hs == hs) { + if (!manager->headsets) manager->default_hs = NULL; else - manager->default_hs = manager->headset_list->data; + manager->default_hs = manager->headsets->data; + + manager_signal(manager->conn, "DefaultHeadsetChanged", + manager->default_hs ? headset_get_path(manager->default_hs) : ""); } + manager_signal(manager->conn, "HeadsetRemoved", headset_get_path(hs)); + + headset_unref(hs); + return send_message_and_unref(manager->conn, reply); } @@ -327,7 +362,7 @@ static DBusHandlerResult am_list_headsets(struct manager *manager, dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); - for (l = manager->headset_list; l != NULL; l = l->next) { + for (l = manager->headsets; l != NULL; l = l->next) { struct headset *hs = l->data; const char *path = headset_get_path(hs); @@ -383,7 +418,7 @@ static DBusHandlerResult am_change_default_headset(struct manager *manager, return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(manager->headset_list, path, headset_path_cmp); + match = g_slist_find_custom(manager->headsets, path, headset_path_cmp); if (!match) return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); @@ -394,6 +429,9 @@ static DBusHandlerResult am_change_default_headset(struct manager *manager, manager->default_hs = match->data; + manager_signal(manager->conn, "DefaultHeadsetChanged", + headset_get_path(manager->default_hs)); + return send_message_and_unref(manager->conn, reply); } @@ -467,11 +505,11 @@ static void manager_free(struct manager *manager) manager->hs_server = NULL; } - if (manager->headset_list) { - g_slist_foreach(manager->headset_list, (GFunc) headset_unref, + if (manager->headsets) { + g_slist_foreach(manager->headsets, (GFunc) headset_unref, manager); - g_slist_free(manager->headset_list); - manager->headset_list = NULL; + g_slist_free(manager->headsets); + manager->headsets = NULL; } dbus_connection_unref(manager->conn); -- cgit From 1f6bcd79a30ce899eec1245956c48876831439eb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 11 Apr 2007 22:46:22 +0000 Subject: Let the manager use global variables --- audio/headset.c | 12 +-- audio/headset.h | 5 +- audio/manager.c | 238 +++++++++++++++++++++++--------------------------------- audio/manager.h | 14 +--- 4 files changed, 107 insertions(+), 162 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index ab4c4d8f..0592d9fa 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -401,8 +401,7 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, - struct manager *manager) +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; struct sockaddr_rc addr; @@ -413,8 +412,6 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, DBusMessage *auth; DBusPendingCall *pending; - assert(manager != NULL); - if (cond & G_IO_NVAL) return FALSE; @@ -434,17 +431,16 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; } - hs = manager_find_headset_by_bda(manager, &addr.rc_bdaddr); + hs = manager_find_headset_by_bda(&addr.rc_bdaddr); if (!hs) { - hs = headset_new(manager_get_dbus_conn(manager), - &addr.rc_bdaddr); + hs = headset_new(manager_get_dbus_conn(), &addr.rc_bdaddr); if (!hs) { error("Unable to create a new headset object"); close(cli_sk); return TRUE; } - manager_add_headset(manager, hs); + manager_add_headset(hs); } if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { diff --git a/audio/headset.h b/audio/headset.h index 7c85b9bb..13532cb7 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -29,8 +29,6 @@ struct headset; -#include "manager.h" - #define BUF_SIZE 1024 struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda); @@ -41,8 +39,7 @@ uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel); int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id); -gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, - struct manager *manager); +gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data); gint headset_path_cmp(gconstpointer headset, gconstpointer path); gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); diff --git a/audio/manager.c b/audio/manager.c index d2f7a015..6c54d314 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -53,17 +53,14 @@ #define SOCKET_NAME "/org/bluez/audio" -struct manager { - DBusConnection *conn; - - /* Headset specific variables */ - GIOChannel *hs_server; - uint32_t hs_record_id; - struct headset *default_hs; - GSList *headsets; -}; +static DBusConnection *connection = NULL; + +static GIOChannel *hs_server = NULL; + +static uint32_t hs_record_id; +static struct headset *default_hs; -static struct manager *manager = NULL; +static GSList *headsets = NULL; static int unix_sock = -1; @@ -78,7 +75,7 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, derr = dbus_message_new_error(msg, name, descr); if (!derr) { - error("Unable to allocate new error return"); + error("Unable to allocate new error return"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -191,65 +188,58 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -static gboolean manager_create_headset_server(struct manager *manager, uint8_t chan) +static gboolean manager_create_headset_server(uint8_t chan) { - assert(manager != NULL); - - if (manager->hs_server) { + if (hs_server) { error("Server socket already created"); return FALSE; } - manager->hs_server = server_socket(&chan); - if (!manager->hs_server) + hs_server = server_socket(&chan); + if (!hs_server) return FALSE; - if (!manager->hs_record_id) - manager->hs_record_id = headset_add_ag_record(manager->conn, chan); + if (!hs_record_id) + hs_record_id = headset_add_ag_record(connection, chan); - if (!manager->hs_record_id) { + if (!hs_record_id) { error("Unable to register service record"); - g_io_channel_unref(manager->hs_server); - manager->hs_server = NULL; + g_io_channel_unref(hs_server); + hs_server = NULL; return FALSE; } - g_io_add_watch(manager->hs_server, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) headset_server_io_cb, manager); + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) headset_server_io_cb, NULL); return TRUE; } -struct headset *manager_find_headset_by_bda(struct manager *manager, bdaddr_t *bda) +struct headset *manager_find_headset_by_bda(bdaddr_t *bda) { GSList *elem; - assert(manager); - - elem = g_slist_find_custom(manager->headsets, bda, headset_bda_cmp); + elem = g_slist_find_custom(headsets, bda, headset_bda_cmp); return elem ? elem->data : NULL; } -void manager_add_headset(struct manager *manager, struct headset *hs) +void manager_add_headset(struct headset *hs) { - assert(manager); assert(hs); - manager->headsets = g_slist_append(manager->headsets, hs); + headsets = g_slist_append(headsets, hs); - manager_signal(manager->conn, "HeadsetCreated", headset_get_path(hs)); + manager_signal(connection, "HeadsetCreated", headset_get_path(hs)); - if (!manager->default_hs) { - manager->default_hs = hs; - manager_signal(manager->conn, "DefaultHeadsetChanged", - headset_get_path(hs)); + if (!default_hs) { + default_hs = hs; + manager_signal(connection, "DefaultHeadsetChanged", + headset_get_path(hs)); } } -static DBusHandlerResult am_create_headset(struct manager *manager, - DBusMessage *msg) +static DBusHandlerResult am_create_headset(DBusMessage *msg) { const char *object_path; const char *address; @@ -262,11 +252,11 @@ static DBusHandlerResult am_create_headset(struct manager *manager, if (!dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_error_is_set(&derr)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } @@ -276,25 +266,24 @@ static DBusHandlerResult am_create_headset(struct manager *manager, return DBUS_HANDLER_RESULT_NEED_MEMORY; str2ba(address, &bda); - hs = manager_find_headset_by_bda(manager, &bda); + hs = manager_find_headset_by_bda(&bda); if (!hs) { - hs = headset_new(manager->conn, &bda); + hs = headset_new(connection, &bda); if (!hs) - return error_reply(manager->conn, msg, + return error_reply(connection, msg, "org.bluez.Error.Failed", "Unable to create new headset object"); - manager_add_headset(manager, hs); + manager_add_headset(hs); } object_path = headset_get_path(hs); dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, DBUS_TYPE_INVALID); - return send_message_and_unref(manager->conn, reply); + return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_remove_headset(struct manager *manager, - DBusMessage *msg) +static DBusHandlerResult am_remove_headset(DBusMessage *msg) { DBusError derr; DBusMessage *reply; @@ -306,18 +295,18 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, if (!dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_error_is_set(&derr)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(manager->headsets, path, headset_path_cmp); + match = g_slist_find_custom(headsets, path, headset_path_cmp); if (!match) - return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); reply = dbus_message_new_method_return(msg); @@ -326,27 +315,26 @@ static DBusHandlerResult am_remove_headset(struct manager *manager, hs = match->data; - manager->headsets = g_slist_remove(manager->headsets, hs); + headsets = g_slist_remove(headsets, hs); - if (manager->default_hs == hs) { - if (!manager->headsets) - manager->default_hs = NULL; + if (default_hs == hs) { + if (!headsets) + default_hs = NULL; else - manager->default_hs = manager->headsets->data; + default_hs = headsets->data; - manager_signal(manager->conn, "DefaultHeadsetChanged", - manager->default_hs ? headset_get_path(manager->default_hs) : ""); + manager_signal(connection, "DefaultHeadsetChanged", + default_hs ? headset_get_path(default_hs) : ""); } - manager_signal(manager->conn, "HeadsetRemoved", headset_get_path(hs)); + manager_signal(connection, "HeadsetRemoved", headset_get_path(hs)); headset_unref(hs); - return send_message_and_unref(manager->conn, reply); + return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_list_headsets(struct manager *manager, - DBusMessage *msg) +static DBusHandlerResult am_list_headsets(DBusMessage *msg) { DBusMessageIter iter; DBusMessageIter array_iter; @@ -362,7 +350,7 @@ static DBusHandlerResult am_list_headsets(struct manager *manager, dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); - for (l = manager->headsets; l != NULL; l = l->next) { + for (l = headsets; l != NULL; l = l->next) { struct headset *hs = l->data; const char *path = headset_get_path(hs); @@ -372,33 +360,31 @@ static DBusHandlerResult am_list_headsets(struct manager *manager, dbus_message_iter_close_container(&iter, &array_iter); - return send_message_and_unref(manager->conn, reply); + return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_get_default_headset(struct manager *manager, - DBusMessage *msg) +static DBusHandlerResult am_get_default_headset(DBusMessage *msg) { DBusMessage *reply; const char *opath; - if (!manager->default_hs) - return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + if (!default_hs) + return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", "There is no default headset"); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - opath = headset_get_path(manager->default_hs); + opath = headset_get_path(default_hs); dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, DBUS_TYPE_INVALID); - return send_message_and_unref(manager->conn, reply); + return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_change_default_headset(struct manager *manager, - DBusMessage *msg) +static DBusHandlerResult am_change_default_headset(DBusMessage *msg) { DBusError derr; DBusMessage *reply; @@ -409,37 +395,36 @@ static DBusHandlerResult am_change_default_headset(struct manager *manager, if (!dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_error_is_set(&derr)) { - err_invalid_args(manager->conn, msg, derr.message); + err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(manager->headsets, path, headset_path_cmp); + match = g_slist_find_custom(headsets, path, headset_path_cmp); if (!match) - return error_reply(manager->conn, msg, "org.bluez.Error.DoesNotExist", + return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - manager->default_hs = match->data; + default_hs = match->data; - manager_signal(manager->conn, "DefaultHeadsetChanged", - headset_get_path(manager->default_hs)); + manager_signal(connection, "DefaultHeadsetChanged", + headset_get_path(default_hs)); - return send_message_and_unref(manager->conn, reply); + return send_message_and_unref(connection, reply); } static DBusHandlerResult am_message(DBusConnection *conn, DBusMessage *msg, void *data) { const char *interface, *member; - struct manager *manager = data; interface = dbus_message_get_interface(msg); member = dbus_message_get_member(msg); @@ -452,19 +437,19 @@ static DBusHandlerResult am_message(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (strcmp(member, "CreateHeadset") == 0) - return am_create_headset(manager, msg); + return am_create_headset(msg); if (strcmp(member, "RemoveHeadset") == 0) - return am_remove_headset(manager, msg); + return am_remove_headset(msg); if (strcmp(member, "ListHeadsets") == 0) - return am_list_headsets(manager, msg); + return am_list_headsets(msg); if (strcmp(member, "DefaultHeadset") == 0) - return am_get_default_headset(manager, msg); + return am_get_default_headset(msg); if (strcmp(member, "ChangeDefaultHeadset") == 0) - return am_change_default_headset(manager, msg); + return am_change_default_headset(msg); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -473,53 +458,9 @@ static const DBusObjectPathVTable am_table = { .message_function = am_message, }; -static struct manager *manager_new(DBusConnection *conn) +DBusConnection *manager_get_dbus_conn(void) { - struct manager *manager; - - manager = g_new0(struct manager, 1); - - if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, - &am_table, manager)) { - error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - g_free(manager); - return NULL; - } - - manager->conn = dbus_connection_ref(conn); - - return manager; -} - -static void manager_free(struct manager *manager) -{ - assert(manager != NULL); - - if (manager->hs_record_id) { - headset_remove_ag_record(manager->conn, manager->hs_record_id); - manager->hs_record_id = 0; - } - - if (manager->hs_server) { - g_io_channel_unref(manager->hs_server); - manager->hs_server = NULL; - } - - if (manager->headsets) { - g_slist_foreach(manager->headsets, (GFunc) headset_unref, - manager); - g_slist_free(manager->headsets); - manager->headsets = NULL; - } - - dbus_connection_unref(manager->conn); - - g_free(manager); -} - -DBusConnection *manager_get_dbus_conn(struct manager *manager) -{ - return manager->conn; + return connection; } int audio_init(DBusConnection *conn) @@ -555,13 +496,16 @@ int audio_init(DBusConnection *conn) g_io_channel_unref(io); - manager = manager_new(conn); - if (!manager) { - error("Failed to create an audio manager"); + if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, + &am_table, NULL)) { + error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); + close(sk); return -1; } - manager_create_headset_server(manager, 12); + connection = dbus_connection_ref(conn); + + manager_create_headset_server(12); return 0; } @@ -572,7 +516,23 @@ void audio_exit(void) unix_sock = -1; - manager_free(manager); + if (hs_record_id) { + headset_remove_ag_record(connection, hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + + if (headsets) { + g_slist_foreach(headsets, (GFunc) headset_unref, NULL); + g_slist_free(headsets); + headsets = NULL; + } + + dbus_connection_unref(connection); - manager = NULL; + connection = NULL; } diff --git a/audio/manager.h b/audio/manager.h index ca4df09d..ee45f7c7 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -20,31 +20,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AUDIO_MANAGER_H -#define __AUDIO_MANAGER_H #include #include -struct manager; - #include "headset.h" #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" -void manager_add_headset(struct manager *manager, struct headset *hs); +void manager_add_headset(struct headset *hs); -struct headset *manager_find_headset_by_bda(struct manager *manager, - bdaddr_t *bda); +struct headset *manager_find_headset_by_bda(bdaddr_t *bda); -DBusConnection *manager_get_dbus_conn(struct manager *manager); +DBusConnection *manager_get_dbus_conn(void); int audio_init(DBusConnection *conn); void audio_exit(void); - -#endif /* __AUDIO_MANAGER_H_ */ - -- cgit From e5aff3f6f7c43f4ddf8d83358d6a49ad2f81e750 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 12 Apr 2007 12:06:33 +0000 Subject: Redo the manager-headset splitup by using object paths as public identifiers for headsets --- audio/headset.c | 350 +++++++++++++++++++++++++++++--------------------------- audio/headset.h | 24 +--- audio/main.c | 4 + audio/manager.c | 90 ++++++--------- audio/manager.h | 2 +- 5 files changed, 225 insertions(+), 245 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0592d9fa..c08489ff 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -98,12 +98,14 @@ struct headset { headset_state_t state; struct pending_connect *pending_connect; - - DBusConnection *conn; }; static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); +static GSList *headsets = NULL; + +static DBusConnection *connection = NULL; + static void pending_connect_free(struct pending_connect *c) { if (c->io) @@ -158,6 +160,99 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); } +static gboolean headset_close_output(struct headset *hs) +{ + assert(hs != NULL); + + if (hs->audio_output == NULL) + return FALSE; + + g_io_channel_close(hs->audio_output); + g_io_channel_unref(hs->audio_output); + hs->audio_output = NULL; + + return TRUE; +} + +/* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ +static gboolean headset_open_output(struct headset *hs, const char *output) +{ + int out; + + assert(hs != NULL && output != NULL); + + headset_close_output(hs); + if (output && hs->output) { + g_free(hs->output); + hs->output = g_strdup(output); + } + + assert(hs->output); + + out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT); + + if (out < 0) { + error("open(%s): %s %d", hs->output, strerror(errno), errno); + return FALSE; + } + + hs->audio_output = g_io_channel_unix_new(out); + if (!hs->audio_output) { + error("Allocating new channel for audio output!"); + return FALSE; + } + + g_io_channel_set_close_on_unref(hs->audio_output, TRUE); + + return TRUE; +} + +static gboolean headset_close_input(struct headset *hs) +{ + assert(hs != NULL); + + if (hs->audio_input == NULL) + return FALSE; + + g_io_channel_close(hs->audio_input); + g_io_channel_unref(hs->audio_input); + hs->audio_input = NULL; + + return TRUE; +} + +#if 0 +static gboolean headset_open_input(struct headset *hs, const char *input) +{ + int in; + + assert(hs != NULL); + + /* we keep the input name, and NULL can be use to reopen */ + if (input && hs->input) { + g_free(hs->input); + hs->input = g_strdup(input); + } + + assert(hs->input); + + in = open(hs->input, O_RDONLY | O_NOCTTY); + + if (in < 0) { + error("open(%s): %s %d", hs->input, strerror(errno), errno); + return FALSE; + } + + hs->audio_input = g_io_channel_unix_new(in); + if (!hs->audio_input) { + error("Allocating new channel for audio input!"); + return FALSE; + } + + return TRUE; +} +#endif + static void hs_signal_gain_setting(struct headset *hs, const char *buf) { const char *name; @@ -192,7 +287,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(hs->conn, signal); + send_message_and_unref(connection, signal); } static void hs_signal(struct headset *hs, const char *name) @@ -205,7 +300,7 @@ static void hs_signal(struct headset *hs, const char *name) return; } - send_message_and_unref(hs->conn, signal); + send_message_and_unref(connection, signal); } static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) @@ -364,7 +459,7 @@ static void send_cancel_auth(struct headset *hs) dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - send_message_and_unref(hs->conn, cancel); + send_message_and_unref(connection, cancel); } static void auth_callback(DBusPendingCall *call, void *data) @@ -406,7 +501,7 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) int srv_sk, cli_sk; struct sockaddr_rc addr; socklen_t size; - char hs_address[18], *address = hs_address; + char hs_address[18], *address = hs_address, *path; const char *uuid = ""; struct headset *hs = NULL; DBusMessage *auth; @@ -431,18 +526,16 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - hs = manager_find_headset_by_bda(&addr.rc_bdaddr); - if (!hs) { - hs = headset_new(manager_get_dbus_conn(), &addr.rc_bdaddr); - if (!hs) { - error("Unable to create a new headset object"); - close(cli_sk); - return TRUE; - } - - manager_add_headset(hs); + /* This returns an existing path if the headset already exists */ + path = headset_add(&addr.rc_bdaddr); + if (!path) { + error("Unable to create a new headset object"); + close(cli_sk); + return TRUE; } + manager_add_headset(path); + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); close(cli_sk); @@ -468,7 +561,7 @@ gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - if (dbus_connection_send_with_reply(hs->conn, auth, &pending, -1) == FALSE) { + if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { error("Sending of authorization request failed"); goto failed; } @@ -618,7 +711,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); } /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ @@ -635,7 +728,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->pending_connect) { - err_connect_failed(hs->conn, hs->pending_connect->msg, err); + err_connect_failed(connection, hs->pending_connect->msg, err); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -694,7 +787,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); } pending_connect_free(hs->pending_connect); @@ -705,7 +798,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he failed: if (hs->pending_connect) { - err_connect_failed(hs->conn, hs->pending_connect->msg, err); + err_connect_failed(connection, hs->pending_connect->msg, err); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -791,14 +884,7 @@ failed: return -1; } -gint headset_path_cmp(gconstpointer headset, gconstpointer path) -{ - const struct headset *hs = headset; - - return strcmp(hs->object_path, path); -} - -gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) +static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) { const struct headset *hs = headset; @@ -866,7 +952,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel) +uint32_t headset_add_ag_record(uint8_t channel) { DBusMessage *msg, *reply; DBusError derr; @@ -890,7 +976,8 @@ uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel) &buf.data, buf.data_size, DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); free(buf.data); dbus_message_unref(msg); @@ -918,7 +1005,7 @@ uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel) return rec_id; } -int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id) +int headset_remove_ag_record(uint32_t rec_id) { DBusMessage *msg, *reply; DBusError derr; @@ -934,7 +1021,8 @@ int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id) DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); dbus_message_unref(msg); @@ -969,7 +1057,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); dbus_error_free(&derr); goto failed; } @@ -979,14 +1067,14 @@ static void get_record_reply(DBusPendingCall *call, void *data) DBUS_TYPE_INVALID)) { error("Unable to get args from GetRecordReply"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } if (!array) { error("Unable to get handle array from reply"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } @@ -994,7 +1082,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (!record) { error("Unable to extract service record from reply"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } @@ -1012,14 +1100,14 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (c->ch == -1) { error("Unable to extract RFCOMM channel from service record"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } if (rfcomm_connect(hs, &err) < 0) { error("Unable to connect"); if (c->msg) - err_connect_failed(hs->conn, c->msg, err); + err_connect_failed(connection, c->msg, err); goto failed; } @@ -1043,7 +1131,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) DBusMessage *reply = NULL; if (!hs || !hs->sco) - return err_not_connected(hs->conn, msg); + return err_not_connected(connection, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1054,7 +1142,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(hs->conn, hs->pending_connect->msg, + err_connect_failed(connection, hs->pending_connect->msg, EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -1064,7 +1152,7 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) close_sco(hs); if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1095,7 +1183,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(hs->conn, hs->pending_connect->msg, + err_connect_failed(connection, hs->pending_connect->msg, EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -1112,7 +1200,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) hs->data_length = 0; if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1136,7 +1224,7 @@ static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg) dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1163,9 +1251,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data) error("GetRemoteServiceHandles failed: %s", derr.message); if (c->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(hs->conn, c->msg, EHOSTDOWN); + err_connect_failed(connection, c->msg, EHOSTDOWN); else - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); } dbus_error_free(&derr); goto failed; @@ -1177,21 +1265,21 @@ static void get_handles_reply(DBusPendingCall *call, void *data) error("Unable to get args from reply"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } if (!array) { error("Unable to get handle array from reply"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); if (c->msg) - err_not_supported(hs->conn, c->msg); + err_not_supported(connection, c->msg); goto failed; } @@ -1204,7 +1292,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (!msg) { error("Unable to allocate new method call"); if (c->msg) - err_connect_failed(hs->conn, c->msg, ENOMEM); + err_connect_failed(connection, c->msg, ENOMEM); goto failed; } @@ -1216,10 +1304,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); if (c->msg) - err_connect_failed(hs->conn, c->msg, EIO); + err_connect_failed(connection, c->msg, EIO); goto failed; } @@ -1278,13 +1366,13 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(hs->conn, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; dbus_message_unref(msg); - return err_connect_failed(hs->conn, msg, EIO); + return err_connect_failed(connection, msg, EIO); } dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); @@ -1339,7 +1427,7 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(hs->conn, msg); + return err_not_connected(connection, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1354,14 +1442,14 @@ static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) if (headset_send_ring(hs) != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(hs->conn, msg); + return err_failed(connection, msg); } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); done: if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1371,7 +1459,7 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(hs->conn, msg); + return err_not_connected(connection, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1389,7 +1477,7 @@ static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) done: if (reply) - send_message_and_unref(hs->conn, reply); + send_message_and_unref(connection, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1401,13 +1489,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) int sk, err; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(hs->conn, msg); /* FIXME: in progress error? */ + return err_not_connected(connection, msg); /* FIXME: in progress error? */ if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) - return err_already_connected(hs->conn, msg); + return err_already_connected(connection, msg); if (hs->sco) - return err_already_connected(hs->conn, msg); + return err_already_connected(connection, msg); c = g_try_new0(struct pending_connect, 1); if (!c) @@ -1421,7 +1509,7 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (sk < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(hs->conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } @@ -1439,13 +1527,13 @@ static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(hs->conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } if (set_nonblocking(sk) < 0) { err = errno; - err_connect_failed(hs->conn, msg, err); + err_connect_failed(connection, msg, err); goto failed; } @@ -1525,15 +1613,17 @@ static const DBusObjectPathVTable hs_table = { .message_function = hs_message, }; -/* -** audio_headset_new: -** Create a unique dbus object path for the headset and allocates a new -** headset or return NULL if fail -*/ -struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda) +char *headset_add(const bdaddr_t *bda) { static int headset_uid = 0; struct headset *hs; + GSList *match; + + match = g_slist_find_custom(headsets, bda, headset_bda_cmp); + if (match) { + hs = match->data; + return hs->object_path; + } hs = g_try_new0(struct headset, 1); if (!hs) { @@ -1544,7 +1634,7 @@ struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda) snprintf(hs->object_path, sizeof(hs->object_path), HEADSET_PATH_BASE "%d", headset_uid++); - if (!dbus_connection_register_object_path(conn, hs->object_path, + if (!dbus_connection_register_object_path(connection, hs->object_path, &hs_table, hs)) { error("D-Bus failed to register %s path", hs->object_path); free (hs); @@ -1553,118 +1643,38 @@ struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda) bacpy(&hs->bda, bda); - hs->conn = dbus_connection_ref(conn); + headsets = g_slist_append(headsets, hs); - return hs; + return g_strdup(hs->object_path); } -void headset_unref(struct headset *hs) +void headset_remove(char *path) { - assert(hs != NULL); + struct headset *hs; + + if (!dbus_connection_get_object_path_data(connection, path, (void *) + &hs)) + return; if (hs->state > HEADSET_STATE_DISCONNECTED) hs_disconnect(hs, NULL); - if (!dbus_connection_unregister_object_path(hs->conn, hs->object_path)) - error("D-Bus failed to unregister %s path", hs->object_path); + if (!dbus_connection_unregister_object_path(connection, path)) + error("D-Bus failed to unregister %s path", path); - dbus_connection_unref(hs->conn); + headsets = g_slist_remove(headsets, hs); g_free(hs); + g_free(path); } -gboolean headset_close_output(struct headset *hs) -{ - assert(hs != NULL); - - if (hs->audio_output == NULL) - return FALSE; - - g_io_channel_close(hs->audio_output); - g_io_channel_unref(hs->audio_output); - hs->audio_output = NULL; - - return TRUE; -} - -/* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ -gboolean headset_open_output(struct headset *hs, const char *output) -{ - int out; - - assert(hs != NULL && output != NULL); - - headset_close_output(hs); - if (output && hs->output) { - g_free(hs->output); - hs->output = g_strdup(output); - } - - assert(hs->output); - - out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT); - - if (out < 0) { - error("open(%s): %s %d", hs->output, strerror(errno), errno); - return FALSE; - } - - hs->audio_output = g_io_channel_unix_new(out); - if (!hs->audio_output) { - error("Allocating new channel for audio output!"); - return FALSE; - } - - g_io_channel_set_close_on_unref(hs->audio_output, TRUE); - - return TRUE; -} - -gboolean headset_close_input(struct headset *hs) -{ - assert(hs != NULL); - - if (hs->audio_input == NULL) - return FALSE; - - g_io_channel_close(hs->audio_input); - g_io_channel_unref(hs->audio_input); - hs->audio_input = NULL; - - return TRUE; -} - -gboolean headset_open_input(struct headset *hs, const char *input) +void headset_init(DBusConnection *conn) { - int in; - - assert(hs != NULL); - - /* we keep the input name, and NULL can be use to reopen */ - if (input && hs->input) { - g_free(hs->input); - hs->input = g_strdup(input); - } - - assert(hs->input); - - in = open(hs->input, O_RDONLY | O_NOCTTY); - - if (in < 0) { - error("open(%s): %s %d", hs->input, strerror(errno), errno); - return FALSE; - } - - hs->audio_input = g_io_channel_unix_new(in); - if (!hs->audio_input) { - error("Allocating new channel for audio input!"); - return FALSE; - } - - return TRUE; + connection = dbus_connection_ref(conn); } -const char *headset_get_path(struct headset *hs) +void headset_exit(void) { - return hs->object_path; + dbus_connection_unref(connection); + connection = NULL; } diff --git a/audio/headset.h b/audio/headset.h index 13532cb7..b8582cef 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -27,31 +27,19 @@ #include -struct headset; - #define BUF_SIZE 1024 -struct headset *headset_new(DBusConnection *conn, const bdaddr_t *bda); +char *headset_add(const bdaddr_t *bda); -void headset_unref(struct headset *hs); +void headset_remove(char *path); -uint32_t headset_add_ag_record(DBusConnection *conn, uint8_t channel); +uint32_t headset_add_ag_record(uint8_t channel); -int headset_remove_ag_record(DBusConnection *conn, uint32_t rec_id); +int headset_remove_ag_record(uint32_t rec_id); gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data); -gint headset_path_cmp(gconstpointer headset, gconstpointer path); -gint headset_bda_cmp(gconstpointer headset, gconstpointer bda); - -const char *headset_get_path(struct headset *hs); - -gboolean headset_close_output(struct headset *hs); - -gboolean headset_open_output(struct headset *hs, const char *output); - -gboolean headset_close_input(struct headset *hs); - -gboolean headset_open_input(struct headset *hs, const char *input); +void headset_init(DBusConnection *conn); +void headset_exit(void); #endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/main.c b/audio/main.c index 6ef3b1c6..3cf609f6 100644 --- a/audio/main.c +++ b/audio/main.c @@ -73,6 +73,8 @@ int main(int argc, char *argv[]) exit(1); } + headset_init(conn); + audio_init(conn); if (argc > 1 && !strcmp(argv[1], "-s")) @@ -82,6 +84,8 @@ int main(int argc, char *argv[]) audio_exit(); + headset_exit(); + dbus_connection_unref(conn); g_main_loop_unref(main_loop); diff --git a/audio/manager.c b/audio/manager.c index 6c54d314..03a6fc47 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -58,7 +58,7 @@ static DBusConnection *connection = NULL; static GIOChannel *hs_server = NULL; static uint32_t hs_record_id; -static struct headset *default_hs; +static char *default_hs = NULL; static GSList *headsets = NULL; @@ -200,7 +200,7 @@ static gboolean manager_create_headset_server(uint8_t chan) return FALSE; if (!hs_record_id) - hs_record_id = headset_add_ag_record(connection, chan); + hs_record_id = headset_add_ag_record(chan); if (!hs_record_id) { error("Unable to register service record"); @@ -215,35 +215,25 @@ static gboolean manager_create_headset_server(uint8_t chan) return TRUE; } -struct headset *manager_find_headset_by_bda(bdaddr_t *bda) +void manager_add_headset(char *path) { - GSList *elem; - - elem = g_slist_find_custom(headsets, bda, headset_bda_cmp); - - return elem ? elem->data : NULL; -} - -void manager_add_headset(struct headset *hs) -{ - assert(hs); + if (g_slist_find_custom(headsets, path, (GCompareFunc) strcmp)) + return; - headsets = g_slist_append(headsets, hs); + headsets = g_slist_append(headsets, path); - manager_signal(connection, "HeadsetCreated", headset_get_path(hs)); + manager_signal(connection, "HeadsetCreated", path); if (!default_hs) { - default_hs = hs; - manager_signal(connection, "DefaultHeadsetChanged", - headset_get_path(hs)); + default_hs = path; + manager_signal(connection, "DefaultHeadsetChanged", path); } } static DBusHandlerResult am_create_headset(DBusMessage *msg) { - const char *object_path; + char *hs_path; const char *address; - struct headset *hs; bdaddr_t bda; DBusMessage *reply; DBusError derr; @@ -266,18 +256,15 @@ static DBusHandlerResult am_create_headset(DBusMessage *msg) return DBUS_HANDLER_RESULT_NEED_MEMORY; str2ba(address, &bda); - hs = manager_find_headset_by_bda(&bda); - if (!hs) { - hs = headset_new(connection, &bda); - if (!hs) - return error_reply(connection, msg, - "org.bluez.Error.Failed", - "Unable to create new headset object"); - manager_add_headset(hs); - } - - object_path = headset_get_path(hs); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &object_path, + /* This returns an existing path if the headset already exists */ + hs_path = headset_add(&bda); + if (!hs_path) + return error_reply(connection, msg, + "org.bluez.Error.Failed", + "Unable to create new headset object"); + manager_add_headset(hs_path); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs_path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); @@ -288,8 +275,7 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) DBusError derr; DBusMessage *reply; GSList *match; - struct headset *hs; - const char *path; + char *path; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -304,7 +290,7 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, headset_path_cmp); + match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); if (!match) return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); @@ -313,23 +299,23 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - hs = match->data; + path = match->data; - headsets = g_slist_remove(headsets, hs); + headsets = g_slist_remove(headsets, path); - if (default_hs == hs) { + if (default_hs == path) { if (!headsets) default_hs = NULL; else default_hs = headsets->data; manager_signal(connection, "DefaultHeadsetChanged", - default_hs ? headset_get_path(default_hs) : ""); + default_hs ? default_hs : ""); } - manager_signal(connection, "HeadsetRemoved", headset_get_path(hs)); + manager_signal(connection, "HeadsetRemoved", path); - headset_unref(hs); + headset_remove(path); return send_message_and_unref(connection, reply); } @@ -350,13 +336,9 @@ static DBusHandlerResult am_list_headsets(DBusMessage *msg) dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); - for (l = headsets; l != NULL; l = l->next) { - struct headset *hs = l->data; - const char *path = headset_get_path(hs); - + for (l = headsets; l != NULL; l = l->next) dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &path); - } + DBUS_TYPE_STRING, &l->data); dbus_message_iter_close_container(&iter, &array_iter); @@ -366,7 +348,6 @@ static DBusHandlerResult am_list_headsets(DBusMessage *msg) static DBusHandlerResult am_get_default_headset(DBusMessage *msg) { DBusMessage *reply; - const char *opath; if (!default_hs) return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", @@ -376,9 +357,7 @@ static DBusHandlerResult am_get_default_headset(DBusMessage *msg) if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - opath = headset_get_path(default_hs); - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &opath, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_hs, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); @@ -404,7 +383,7 @@ static DBusHandlerResult am_change_default_headset(DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, headset_path_cmp); + match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); if (!match) return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", "The headset does not exist"); @@ -415,8 +394,7 @@ static DBusHandlerResult am_change_default_headset(DBusMessage *msg) default_hs = match->data; - manager_signal(connection, "DefaultHeadsetChanged", - headset_get_path(default_hs)); + manager_signal(connection, "DefaultHeadsetChanged", default_hs); return send_message_and_unref(connection, reply); } @@ -517,7 +495,7 @@ void audio_exit(void) unix_sock = -1; if (hs_record_id) { - headset_remove_ag_record(connection, hs_record_id); + headset_remove_ag_record(hs_record_id); hs_record_id = 0; } @@ -527,7 +505,7 @@ void audio_exit(void) } if (headsets) { - g_slist_foreach(headsets, (GFunc) headset_unref, NULL); + g_slist_foreach(headsets, (GFunc) headset_remove, NULL); g_slist_free(headsets); headsets = NULL; } diff --git a/audio/manager.h b/audio/manager.h index ee45f7c7..9a9948ff 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -31,7 +31,7 @@ #define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" -void manager_add_headset(struct headset *hs); +void manager_add_headset(char *path); struct headset *manager_find_headset_by_bda(bdaddr_t *bda); -- cgit From e41abf4a7c4f97c679ce748ef7eabbd11bcff3d9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 12 Apr 2007 14:05:18 +0000 Subject: Remove unused functions --- audio/manager.c | 5 ----- audio/manager.h | 4 ---- 2 files changed, 9 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 03a6fc47..3265c89e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -436,11 +436,6 @@ static const DBusObjectPathVTable am_table = { .message_function = am_message, }; -DBusConnection *manager_get_dbus_conn(void) -{ - return connection; -} - int audio_init(DBusConnection *conn) { GIOChannel *io; diff --git a/audio/manager.h b/audio/manager.h index 9a9948ff..4b5975a8 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -33,10 +33,6 @@ void manager_add_headset(char *path); -struct headset *manager_find_headset_by_bda(bdaddr_t *bda); - -DBusConnection *manager_get_dbus_conn(void); - int audio_init(DBusConnection *conn); void audio_exit(void); -- cgit From a07ea139e52ab9733f6d1e9f0ab2e0bf232059ef Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 12 Apr 2007 14:26:55 +0000 Subject: A little more refactoring --- audio/headset.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- audio/headset.h | 7 +---- audio/main.c | 14 ++++++--- audio/manager.c | 93 -------------------------------------------------------- 4 files changed, 102 insertions(+), 106 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c08489ff..a6581df8 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -54,6 +54,8 @@ #include "manager.h" #include "headset.h" +#define DEFAULT_HS_AG_CHANNEL 12 + #define RING_INTERVAL 3000 typedef enum { @@ -102,6 +104,10 @@ struct headset { static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); +static GIOChannel *hs_server = NULL; + +static uint32_t hs_record_id = 0; + static GSList *headsets = NULL; static DBusConnection *connection = NULL; @@ -496,7 +502,7 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; struct sockaddr_rc addr; @@ -952,7 +958,7 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -uint32_t headset_add_ag_record(uint8_t channel) +static uint32_t headset_add_ag_record(uint8_t channel) { DBusMessage *msg, *reply; DBusError derr; @@ -1668,13 +1674,95 @@ void headset_remove(char *path) g_free(path); } -void headset_init(DBusConnection *conn) +static GIOChannel *server_socket(uint8_t *channel) +{ + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +int headset_init(DBusConnection *conn) { + uint8_t chan = DEFAULT_HS_AG_CHANNEL; + connection = dbus_connection_ref(conn); + + hs_server = server_socket(&chan); + if (!hs_server) + return -1; + + if (!hs_record_id) + hs_record_id = headset_add_ag_record(chan); + + if (!hs_record_id) { + error("Unable to register service record"); + g_io_channel_unref(hs_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) headset_server_io_cb, NULL); + + return 0; } void headset_exit(void) { + if (hs_record_id) { + headset_remove_ag_record(hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + dbus_connection_unref(connection); connection = NULL; } diff --git a/audio/headset.h b/audio/headset.h index b8582cef..888b53fe 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -33,13 +33,8 @@ char *headset_add(const bdaddr_t *bda); void headset_remove(char *path); -uint32_t headset_add_ag_record(uint8_t channel); +int headset_init(DBusConnection *conn); -int headset_remove_ag_record(uint32_t rec_id); - -gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data); - -void headset_init(DBusConnection *conn); void headset_exit(void); #endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/main.c b/audio/main.c index 3cf609f6..470b26d5 100644 --- a/audio/main.c +++ b/audio/main.c @@ -73,19 +73,25 @@ int main(int argc, char *argv[]) exit(1); } - headset_init(conn); + if (audio_init(conn) < 0) { + error("Audio init failed!"); + exit(1); + } - audio_init(conn); + if (headset_init(conn) < 0) { + error("Headset initialization failed!"); + exit(1); + } if (argc > 1 && !strcmp(argv[1], "-s")) register_external_service(conn, "audio", "Audio service", ""); g_main_loop_run(main_loop); - audio_exit(); - headset_exit(); + audio_exit(); + dbus_connection_unref(conn); g_main_loop_unref(main_loop); diff --git a/audio/manager.c b/audio/manager.c index 3265c89e..07aa7b60 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -55,9 +55,6 @@ static DBusConnection *connection = NULL; -static GIOChannel *hs_server = NULL; - -static uint32_t hs_record_id; static char *default_hs = NULL; static GSList *headsets = NULL; @@ -137,84 +134,6 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } -static GIOChannel *server_socket(uint8_t *channel) -{ - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; -} - -static gboolean manager_create_headset_server(uint8_t chan) -{ - if (hs_server) { - error("Server socket already created"); - return FALSE; - } - - hs_server = server_socket(&chan); - if (!hs_server) - return FALSE; - - if (!hs_record_id) - hs_record_id = headset_add_ag_record(chan); - - if (!hs_record_id) { - error("Unable to register service record"); - g_io_channel_unref(hs_server); - hs_server = NULL; - return FALSE; - } - - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) headset_server_io_cb, NULL); - - return TRUE; -} - void manager_add_headset(char *path) { if (g_slist_find_custom(headsets, path, (GCompareFunc) strcmp)) @@ -478,8 +397,6 @@ int audio_init(DBusConnection *conn) connection = dbus_connection_ref(conn); - manager_create_headset_server(12); - return 0; } @@ -489,16 +406,6 @@ void audio_exit(void) unix_sock = -1; - if (hs_record_id) { - headset_remove_ag_record(hs_record_id); - hs_record_id = 0; - } - - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; - } - if (headsets) { g_slist_foreach(headsets, (GFunc) headset_remove, NULL); g_slist_free(headsets); -- cgit From 260b175f47f5006b34a8eceda9914f67ef0caa12 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 13 Apr 2007 11:51:00 +0000 Subject: Cleanup and small fixes --- audio/headset.c | 233 +++++++++++++++++++++++++++++++------------------------- audio/headset.h | 6 +- audio/main.c | 4 +- audio/manager.c | 41 ++++++---- audio/manager.h | 2 +- 5 files changed, 162 insertions(+), 124 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a6581df8..b1aad63d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -166,6 +166,13 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); } +static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) +{ + const struct headset *hs = headset; + + return bacmp(&hs->bda, bda); +} + static gboolean headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -502,92 +509,6 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) -{ - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; - char hs_address[18], *address = hs_address, *path; - const char *uuid = ""; - struct headset *hs = NULL; - DBusMessage *auth; - DBusPendingCall *pending; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - /* This returns an existing path if the headset already exists */ - path = headset_add(&addr.rc_bdaddr); - if (!path) { - error("Unable to create a new headset object"); - close(cli_sk); - return TRUE; - } - - manager_add_headset(path); - - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { - debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; - } - - hs->rfcomm = g_io_channel_unix_new(cli_sk); - if (!hs->rfcomm) { - error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; - } - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocat new RequestAuthorization method call"); - goto failed; - } - - ba2str(&hs->bda, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { - error("Sending of authorization request failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); - - return TRUE; - -failed: - if (hs->rfcomm) { - g_io_channel_close(hs->rfcomm); - g_io_channel_unref(hs->rfcomm); - hs->rfcomm = NULL; - } - - return TRUE; -} - static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct headset *hs = data; @@ -890,13 +811,6 @@ failed: return -1; } -static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) -{ - const struct headset *hs = headset; - - return bacmp(&hs->bda, bda); -} - static int create_ag_record(sdp_buf_t *buf, uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -1619,17 +1533,15 @@ static const DBusObjectPathVTable hs_table = { .message_function = hs_message, }; -char *headset_add(const bdaddr_t *bda) +static struct headset *headset_add_internal(const bdaddr_t *bda) { static int headset_uid = 0; struct headset *hs; GSList *match; match = g_slist_find_custom(headsets, bda, headset_bda_cmp); - if (match) { - hs = match->data; - return hs->object_path; - } + if (match) + return match->data; hs = g_try_new0(struct headset, 1); if (!hs) { @@ -1651,15 +1563,40 @@ char *headset_add(const bdaddr_t *bda) headsets = g_slist_append(headsets, hs); - return g_strdup(hs->object_path); + return hs; } -void headset_remove(char *path) +const char *headset_add(const bdaddr_t *bda) { struct headset *hs; - if (!dbus_connection_get_object_path_data(connection, path, (void *) - &hs)) + hs = headset_add_internal(bda); + if (!hs) + return NULL; + + return hs->object_path; +} + +const char *headset_get(const bdaddr_t *bda) +{ + GSList *match; + struct headset *hs; + + match = g_slist_find_custom(headsets, bda, headset_bda_cmp); + if (!match) + return NULL; + + hs = match->data; + + return hs->object_path; +} + +void headset_remove(const char *path) +{ + struct headset *hs; + + if (!dbus_connection_get_object_path_data(connection, path, + (void *) &hs)) return; if (hs->state > HEADSET_STATE_DISCONNECTED) @@ -1671,7 +1608,97 @@ void headset_remove(char *path) headsets = g_slist_remove(headsets, hs); g_free(hs); - g_free(path); +} + +static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +{ + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + char hs_address[18], *address = hs_address; + const char *uuid = ""; + struct headset *hs = NULL; + DBusMessage *auth; + DBusPendingCall *pending; + GSList *match; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + match = g_slist_find_custom(headsets, &addr.rc_bdaddr, headset_bda_cmp); + if (!match) { + hs = headset_add_internal(&addr.rc_bdaddr); + if (!hs) { + error("Unable to create a new headset object"); + close(cli_sk); + return TRUE; + } + + manager_add_headset(hs->object_path); + } + else + hs = match->data; + + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + hs->rfcomm = g_io_channel_unix_new(cli_sk); + if (!hs->rfcomm) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + return TRUE; + } + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocat new RequestAuthorization method call"); + goto failed; + } + + ba2str(&hs->bda, hs_address); + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { + error("Sending of authorization request failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(auth); + + return TRUE; + +failed: + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); + hs->rfcomm = NULL; + } + + return TRUE; } static GIOChannel *server_socket(uint8_t *channel) diff --git a/audio/headset.h b/audio/headset.h index 888b53fe..e51236ad 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -29,9 +29,11 @@ #define BUF_SIZE 1024 -char *headset_add(const bdaddr_t *bda); +const char *headset_get(const bdaddr_t *bda); -void headset_remove(char *path); +const char *headset_add(const bdaddr_t *bda); + +void headset_remove(const char *path); int headset_init(DBusConnection *conn); diff --git a/audio/main.c b/audio/main.c index 470b26d5..2188e6e7 100644 --- a/audio/main.c +++ b/audio/main.c @@ -88,10 +88,10 @@ int main(int argc, char *argv[]) g_main_loop_run(main_loop); - headset_exit(); - audio_exit(); + headset_exit(); + dbus_connection_unref(conn); g_main_loop_unref(main_loop); diff --git a/audio/manager.c b/audio/manager.c index 07aa7b60..87dd4b01 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -134,24 +134,29 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } -void manager_add_headset(char *path) +void manager_add_headset(const char *path) { - if (g_slist_find_custom(headsets, path, (GCompareFunc) strcmp)) - return; + char *my_path = g_strdup(path); - headsets = g_slist_append(headsets, path); + headsets = g_slist_append(headsets, my_path); - manager_signal(connection, "HeadsetCreated", path); + manager_signal(connection, "HeadsetCreated", my_path); if (!default_hs) { - default_hs = path; - manager_signal(connection, "DefaultHeadsetChanged", path); + default_hs = my_path; + manager_signal(connection, "DefaultHeadsetChanged", my_path); } } +static void manager_remove_headset(char *path) +{ + headset_remove(path); + g_free(path); +} + static DBusHandlerResult am_create_headset(DBusMessage *msg) { - char *hs_path; + const char *hs_path; const char *address; bdaddr_t bda; DBusMessage *reply; @@ -175,13 +180,16 @@ static DBusHandlerResult am_create_headset(DBusMessage *msg) return DBUS_HANDLER_RESULT_NEED_MEMORY; str2ba(address, &bda); - /* This returns an existing path if the headset already exists */ - hs_path = headset_add(&bda); - if (!hs_path) - return error_reply(connection, msg, - "org.bluez.Error.Failed", - "Unable to create new headset object"); - manager_add_headset(hs_path); + + hs_path = headset_get(&bda); + if (!hs_path) { + hs_path = headset_add(&bda); + if (!hs_path) + return error_reply(connection, msg, + "org.bluez.Error.Failed", + "Unable to create new headset object"); + manager_add_headset(hs_path); + } dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs_path, DBUS_TYPE_INVALID); @@ -221,6 +229,7 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) path = match->data; headsets = g_slist_remove(headsets, path); + g_free(path); if (default_hs == path) { if (!headsets) @@ -407,7 +416,7 @@ void audio_exit(void) unix_sock = -1; if (headsets) { - g_slist_foreach(headsets, (GFunc) headset_remove, NULL); + g_slist_foreach(headsets, (GFunc) manager_remove_headset, NULL); g_slist_free(headsets); headsets = NULL; } diff --git a/audio/manager.h b/audio/manager.h index 4b5975a8..aae34101 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -31,7 +31,7 @@ #define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" -void manager_add_headset(char *path); +void manager_add_headset(const char *path); int audio_init(DBusConnection *conn); -- cgit From 0f00d6b4c4aa60dc1551afaeef2a739ab3ff1a25 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 13 Apr 2007 12:06:54 +0000 Subject: Add headset.h to +bluetoothd_service_audio_SOURCES --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 6bf33050..80f62b28 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -10,7 +10,7 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio -bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.c ipc.h +bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.h headset.c ipc.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -- cgit From 77c58d8b6b0c63b8dc2201dfd9019e7f824e63e5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Apr 2007 13:55:27 +0000 Subject: Only stop indicating a call if the answer button is pressed (not e.g. if the volume button is pressed) --- audio/headset.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b1aad63d..4a7e8db4 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -408,6 +408,11 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, break; case HEADSET_EVENT_KEYPRESS: + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } + hs_signal(hs, "AnswerRequested"); break; @@ -438,11 +443,6 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->data_start = 0; } - if (hs->ring_timer) { - g_source_remove(hs->ring_timer); - hs->ring_timer = 0; - } - return TRUE; failed: -- cgit From ed805ba6dc4ab8315e6fd83ecadbe57112dc93b5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Apr 2007 14:07:49 +0000 Subject: Cleanup the headset event parsing function a little --- audio/headset.c | 93 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 48 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 4a7e8db4..9189177b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -359,10 +359,11 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) { unsigned char buf[BUF_SIZE]; - char *cr; + char *cr, rsp[BUF_SIZE]; gsize bytes_read = 0; - gsize free_space; + gsize free_space, count, bytes_written, total_bytes_written; GIOError err; + off_t cmd_len; if (cond & G_IO_NVAL) return FALSE; @@ -391,58 +392,54 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->buf[hs->data_start + hs->data_length] = '\0'; cr = strchr(&hs->buf[hs->data_start], '\r'); - if (cr) { - char rsp[BUF_SIZE]; - gsize count, bytes_written, total_bytes_written; - off_t cmd_len; - - cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; - *cr = '\0'; - - memset(rsp, 0, sizeof(rsp)); - - /* FIXME: make a better parse function */ - switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { - case HEADSET_EVENT_GAIN: - hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); - break; - - case HEADSET_EVENT_KEYPRESS: - if (hs->ring_timer) { - g_source_remove(hs->ring_timer); - hs->ring_timer = 0; - } - - hs_signal(hs, "AnswerRequested"); - break; - - case HEADSET_EVENT_INVALID: - case HEADSET_EVENT_UNKNOWN: - default: - debug("Unknown headset event"); - break; - } + if (!cr) + return TRUE; + + cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; + *cr = '\0'; - count = strlen(rsp); - total_bytes_written = bytes_written = 0; - err = G_IO_ERROR_NONE; + memset(rsp, 0, sizeof(rsp)); + + switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { + case HEADSET_EVENT_GAIN: + hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); + break; - while (err == G_IO_ERROR_NONE && total_bytes_written < count) { - /* FIXME: make it async */ - err = g_io_channel_write(hs->rfcomm, rsp + total_bytes_written, - count - total_bytes_written, &bytes_written); - if (err != G_IO_ERROR_NONE) - error("Error while writting to the audio output channel"); - total_bytes_written += bytes_written; - }; + case HEADSET_EVENT_KEYPRESS: + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } - hs->data_start += cmd_len; - hs->data_length -= cmd_len; + hs_signal(hs, "AnswerRequested"); + break; - if (!hs->data_length) - hs->data_start = 0; + case HEADSET_EVENT_INVALID: + case HEADSET_EVENT_UNKNOWN: + default: + debug("Unknown headset event"); + break; } + count = strlen(rsp); + total_bytes_written = bytes_written = 0; + err = G_IO_ERROR_NONE; + + while (err == G_IO_ERROR_NONE && total_bytes_written < count) { + /* FIXME: make it async */ + err = g_io_channel_write(hs->rfcomm, rsp + total_bytes_written, + count - total_bytes_written, &bytes_written); + if (err != G_IO_ERROR_NONE) + error("Error while writting to the audio output channel"); + total_bytes_written += bytes_written; + }; + + hs->data_start += cmd_len; + hs->data_length -= cmd_len; + + if (!hs->data_length) + hs->data_start = 0; + return TRUE; failed: -- cgit From 4844b0c1d3a2f648b1c0957a3ef2551407be79b9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 20 Apr 2007 09:11:35 +0000 Subject: Check service classes to be sure that we got the right service record --- audio/headset.c | 53 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 9189177b..81115915 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -961,7 +961,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) uint8_t *array; int array_len, record_len, err = EIO; sdp_record_t *record = NULL; - sdp_list_t *protos; + sdp_list_t *protos, *classes = NULL; + uuid_t uuid; struct headset *hs = data; struct pending_connect *c; @@ -973,40 +974,50 @@ static void get_record_reply(DBusPendingCall *call, void *data) dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceRecord failed: %s", derr.message); - if (c->msg) - err_not_supported(connection, c->msg); dbus_error_free(&derr); - goto failed; + goto failed_not_supported; } if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, DBUS_TYPE_INVALID)) { error("Unable to get args from GetRecordReply"); - if (c->msg) - err_not_supported(connection, c->msg); - goto failed; + goto failed_not_supported; } if (!array) { error("Unable to get handle array from reply"); - if (c->msg) - err_not_supported(connection, c->msg); - goto failed; + goto failed_not_supported; } record = sdp_extract_pdu(array, &record_len); if (!record) { error("Unable to extract service record from reply"); - if (c->msg) - err_not_supported(connection, c->msg); - goto failed; + goto failed_not_supported; } if (record_len != array_len) debug("warning: array len (%d) != record len (%d)", array_len, record_len); + if (sdp_get_service_classes(record, &classes) < 0) { + error("Unable to get service classes from record"); + goto failed_not_supported; + } + + memcpy(&uuid, classes->data, sizeof(uuid)); + + if (!sdp_uuid128_to_uuid(&uuid)) { + error("Not a 16 bit UUID"); + goto failed_not_supported; + } + + if ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != HEADSET_SVCLASS_ID) || + (uuid.type == SDP_UUID16 && uuid.value.uuid16 != HEADSET_SVCLASS_ID)) { + error("Service classes did not contain the expected UUID"); + goto failed_not_supported; + } + if (!sdp_get_access_protos(record, &protos)) { c->ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); @@ -1016,9 +1027,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (c->ch == -1) { error("Unable to extract RFCOMM channel from service record"); - if (c->msg) - err_not_supported(connection, c->msg); - goto failed; + goto failed_not_supported; } if (rfcomm_connect(hs, &err) < 0) { @@ -1028,12 +1037,18 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed; } + sdp_list_free(classes, free); sdp_record_free(record); dbus_message_unref(reply); return; +failed_not_supported: + if (c->msg) + err_not_supported(connection, c->msg); failed: + if (classes) + sdp_list_free(classes, free); if (record) sdp_record_free(record); if (reply) @@ -1251,10 +1266,8 @@ static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) assert(hs != NULL); - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) { - error("Already connected"); - return DBUS_HANDLER_RESULT_HANDLED; - } + if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) + return err_already_connected(connection, msg); hs->pending_connect = g_try_new0(struct pending_connect, 1); if (!hs->pending_connect) { -- cgit From f5dcb96ae5e257efc4dc9cdd510119e9788d1228 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 20 Apr 2007 12:15:20 +0000 Subject: Free path string first when it is no longer needed --- audio/manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 87dd4b01..1512218f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -229,7 +229,6 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) path = match->data; headsets = g_slist_remove(headsets, path); - g_free(path); if (default_hs == path) { if (!headsets) @@ -245,6 +244,8 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) headset_remove(path); + g_free(path); + return send_message_and_unref(connection, reply); } -- cgit From 37cf1720b4b90bf2ad23b07d89c641959dcd9cab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Apr 2007 05:27:50 +0000 Subject: Fix some debug outputs --- audio/ctl_bluetooth.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 037cfb09..bcc37659 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -108,7 +108,7 @@ static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext, static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int *type, unsigned int *acc, unsigned int *count) { - DBG("ext %p key %td", ext, key); + DBG("ext %p key %ld", ext, key); *type = SND_CTL_ELEM_TYPE_INTEGER; *acc = SND_CTL_EXT_ACCESS_READWRITE; @@ -120,7 +120,7 @@ static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *imin, long *imax, long *istep) { - DBG("ext %p key %td", ext, key); + DBG("ext %p key %ld", ext, key); *istep = 1; *imin = BLUETOOTH_MINVOL; @@ -136,7 +136,7 @@ static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char buf[] = { 0x00, 0x00 }; int len; - DBG("ext %p key %td", ext, key); + DBG("ext %p key %ld", ext, key); len = write(data->sock, buf, sizeof(buf)); @@ -152,7 +152,7 @@ static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned char buf[] = { 0xff, 0xff }; int len; - DBG("ext %p key %td", ext, key); + DBG("ext %p key %ld", ext, key); len = write(data->sock, buf, sizeof(buf)); -- cgit From 6ecc27526933015c672c305669e6f09c68a0b573 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Apr 2007 14:18:09 +0000 Subject: Use org.bluez.audio.Error interface for D-Bus error returns --- audio/headset.c | 11 ++++++----- audio/manager.c | 13 ++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 81115915..04a24ca0 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -140,30 +140,31 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, "org.bluez.Error.AlreadyConnected", + return error_reply(conn, msg, "org.bluez.audio.Error.AlreadyConnected", "Already connected to a device"); } static DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, "org.bluez.Error.NotConnected", + return error_reply(conn, msg, "org.bluez.audio.Error.NotConnected", "Not connected to any device"); } static DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, "org.bluez.Error.NotSupported", + return error_reply(conn, msg, "org.bluez.audio.Error.NotSupported", "The service is not supported by the remote device"); } static DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *msg, int err) { - return error_reply(conn, msg, "org.bluez.Error.ConnectFailed", strerror(err)); + return error_reply(conn, msg, "org.bluez.audio.Error.ConnectFailed", + strerror(err)); } static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, "org.bluez.Error.Failed", "Failed"); + return error_reply(conn, msg, "org.bluez.audio.Error.Failed", "Failed"); } static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) diff --git a/audio/manager.c b/audio/manager.c index 1512218f..04bb3b96 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -82,7 +82,7 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, const char *descr) { - return error_reply(conn, msg, "org.bluez.Error.InvalidArguments", + return error_reply(conn, msg, "org.bluez.audio.Error.InvalidArguments", descr ? descr : "Invalid arguments in method call"); } @@ -186,7 +186,7 @@ static DBusHandlerResult am_create_headset(DBusMessage *msg) hs_path = headset_add(&bda); if (!hs_path) return error_reply(connection, msg, - "org.bluez.Error.Failed", + "org.bluez.audio.Error.Failed", "Unable to create new headset object"); manager_add_headset(hs_path); } @@ -219,7 +219,8 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); if (!match) - return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", + return error_reply(connection, msg, + "org.bluez.audio.Error.DoesNotExist", "The headset does not exist"); reply = dbus_message_new_method_return(msg); @@ -279,7 +280,8 @@ static DBusHandlerResult am_get_default_headset(DBusMessage *msg) DBusMessage *reply; if (!default_hs) - return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", + return error_reply(connection, msg, + "org.bluez.audio.Error.DoesNotExist", "There is no default headset"); reply = dbus_message_new_method_return(msg); @@ -314,7 +316,8 @@ static DBusHandlerResult am_change_default_headset(DBusMessage *msg) match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); if (!match) - return error_reply(connection, msg, "org.bluez.Error.DoesNotExist", + return error_reply(connection, msg, + "org.bluez.audio.Error.DoesNotExist", "The headset does not exist"); reply = dbus_message_new_method_return(msg); -- cgit From 36b30565da2a137e3b0ae0012d846bce6c4c7457 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 May 2007 08:31:05 +0000 Subject: Make use of FinishRemoteServiceTransaction method --- audio/headset.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 04a24ca0..56677f3f 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -955,6 +955,41 @@ int headset_remove_ag_record(uint32_t rec_id) return 0; } +static void finish_sdp_transaction(bdaddr_t *dba) +{ + char address[18], *addr_ptr = address; + DBusMessage *msg, *reply; + DBusError derr; + + ba2str(dba, address); + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "FinishRemoteServiceTransaction"); + if (!msg) { + error("Unable to allocate new method call"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { + error("FinishRemoteServiceTransaction(%s) failed: %s", + address, derr.message); + dbus_error_free(&derr); + return; + } + + dbus_message_unref(reply); +} + static void get_record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; @@ -1042,6 +1077,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); + finish_sdp_transaction(&hs->bda); + return; failed_not_supported: @@ -1057,6 +1094,7 @@ failed: pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; + finish_sdp_transaction(&hs->bda); } static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) -- cgit From a2174a2890f2601b06ed67f593d1227e41f1497e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 May 2007 12:38:00 +0000 Subject: Implement IsPlaying method --- audio/headset.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 56677f3f..971a68c1 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1128,6 +1128,30 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult hs_is_playing(struct headset *hs, DBusMessage *msg) +{ + DBusMessage *reply; + dbus_bool_t playing; + + assert(hs); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (hs->state == HEADSET_STATE_PLAYING) + playing = TRUE; + else + playing = FALSE; + + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) { DBusMessage *reply = NULL; @@ -1575,6 +1599,9 @@ static DBusHandlerResult hs_message(DBusConnection *conn, if (strcmp(member, "Stop") == 0) return hs_stop(hs, msg); + if (strcmp(member, "IsPlaying") == 0) + return hs_is_playing(hs, msg); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -- cgit From ce665805af418b5565eefd961f60a66d1a183a3d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 May 2007 12:38:45 +0000 Subject: Update API description to match current implementation --- audio/audio-api.txt | 74 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 25 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index e123689e..497c6cb6 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -23,6 +23,10 @@ Methods array{string} ListHeadsets() Returns the object path for the default headset device. + void ChangeDefaultHeadset(string path) + + Changes the default headset. + string CreateHeadset(string address) Create a new headset device and returns @@ -30,12 +34,21 @@ Methods array{string} ListHeadsets() void RemoveHeadset(string path) + Removes a headset object and all information + related to it. + Signals void HeadsetCreated(string path) + Sent when a new headset object has been created. + void HeadsetRemoved(string path) + Sent when a headset object has been removed. + void DefaultHeadsetChanged(string path) + Sent when the default headset has changed. + Audio Headset hierarchy ======================= @@ -44,28 +57,12 @@ Service org.bluez.audio Interface org.bluez.audio.Headset Object path /org/bluez/audio/headset* -Methods string GetAddress() - - string GetName() - - void Connect() +Methods void Connect() void Disconnect() boolean IsConnected() - uint16 GetSpeakerGain() - - uint16 GetMicrophoneGain() - - void SetSpeakerGain(uint16 gain) - - Gain is 0..15 - - void SetMicrophoneGain(uint16 gain) - - Gain is 0..15 - void IndicateCall() Indicate an incoming call on the headset @@ -76,18 +73,45 @@ Methods string GetAddress() Cancel the incoming call indication. -Signals void SpeakerGainChanged(uint16 gain) + void Play() + + Open the audio connection to the headset. - The speaker gain changed. + void Stop() - void MicrophoneGainChanged(uint16 gain) + Close the audio connection. - The microphone gain changed. + boolean IsPlaying() + + Returns true if an audio connection to the headset + is active. + + +Signals void AnswerRequested() + + Sent when the answer button is pressed on the headset + + void Connected() + + Sent when the device has been connected to. + + void Disconnected() - void CallIndicated() + Sent when the device has been disconnected from. - An incoming call was indicated. + void Stopped() - void CallCanceled() + Sent when the audio connection is closed + + void Playing() + + Sent when the audio connection is opened + + void SpeakerGainChanged(uint16 gain) + + The speaker gain changed. + + void MicrophoneGainChanged(uint16 gain) + + The microphone gain changed. - Incoming call was canceled. -- cgit From bbbc61a634179156b2a64d95f0ddd25fd164eaaf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 May 2007 15:51:02 +0000 Subject: Convert to using generic dbus message dispatching --- audio/headset.c | 132 +++++++++++++++++++++++++++++--------------------------- audio/headset.h | 2 + audio/manager.c | 84 ++++++++++++++++++------------------ audio/manager.h | 1 + 4 files changed, 114 insertions(+), 105 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 971a68c1..7b043af3 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -50,6 +50,7 @@ #include #include "dbus.h" +#include "dbus-helper.h" #include "logging.h" #include "manager.h" #include "headset.h" @@ -102,7 +103,8 @@ struct headset { struct pending_connect *pending_connect; }; -static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg); +static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, + void *data); static GIOChannel *hs_server = NULL; @@ -446,7 +448,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->sco) close_sco(hs); - hs_disconnect(hs, NULL); + hs_disconnect(NULL, NULL, hs); return FALSE; } @@ -1097,8 +1099,10 @@ failed: finish_sdp_transaction(&hs->bda); } -static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply = NULL; if (!hs || !hs->sco) @@ -1128,8 +1132,10 @@ static DBusHandlerResult hs_stop(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_is_playing(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply; dbus_bool_t playing; @@ -1152,8 +1158,10 @@ static DBusHandlerResult hs_is_playing(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply = NULL; char hs_address[18]; @@ -1166,7 +1174,7 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) } if (hs->state > HEADSET_STATE_CONNECTED) - hs_stop(hs, NULL); + hs_stop(NULL, NULL, hs); if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); @@ -1200,8 +1208,10 @@ static DBusHandlerResult hs_disconnect(struct headset *hs, DBusMessage *msg) return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_is_connected(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply; dbus_bool_t connected; @@ -1317,12 +1327,14 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - hs_disconnect(hs, NULL); + hs_disconnect(NULL, NULL, hs); } -static DBusHandlerResult hs_connect(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, + void *data) { DBusPendingCall *pending; + struct headset *hs = data; const char *hs_svc = "hsp"; const char *addr_ptr; char hs_address[18]; @@ -1413,8 +1425,10 @@ static gboolean ring_timer_cb(gpointer data) return TRUE; } -static DBusHandlerResult hs_ring(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply = NULL; assert(hs != NULL); @@ -1447,8 +1461,10 @@ done: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_cancel_ringing(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) @@ -1475,8 +1491,10 @@ done: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_play(struct headset *hs, DBusMessage *msg) +static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, + void *data) { + struct headset *hs = data; struct sockaddr_sco addr; struct pending_connect *c; int sk, err; @@ -1560,53 +1578,27 @@ failed: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct headset *hs = data; - const char *interface, *member; - - assert(hs != NULL); - - interface = dbus_message_get_interface(msg); - member = dbus_message_get_member(msg); - - if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && - !strcmp("Introspect", member)) - return simple_introspect(conn, msg, data); - - if (strcmp(interface, "org.bluez.audio.Headset") != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(member, "Connect") == 0) - return hs_connect(hs, msg); - - if (strcmp(member, "Disconnect") == 0) - return hs_disconnect(hs, msg); - - if (strcmp(member, "IsConnected") == 0) - return hs_is_connected(hs, msg); - - if (strcmp(member, "IndicateCall") == 0) - return hs_ring(hs, msg); - - if (strcmp(member, "CancelCall") == 0) - return hs_cancel_ringing(hs, msg); - - if (strcmp(member, "Play") == 0) - return hs_play(hs, msg); - - if (strcmp(member, "Stop") == 0) - return hs_stop(hs, msg); - - if (strcmp(member, "IsPlaying") == 0) - return hs_is_playing(hs, msg); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} +static DBusMethodVTable headset_methods[] = { + { "Connect", hs_connect, "", "" }, + { "Disconnect", hs_disconnect, "", "" }, + { "IsConnected", hs_is_connected, "", "b" }, + { "IndicateCall", hs_ring, "", "" }, + { "CancelCall", hs_cancel_ringing, "", "" }, + { "Play", hs_play, "", "" }, + { "Stop", hs_stop, "", "" }, + { "IsPlaying", hs_is_playing, "", "b" }, + { NULL, NULL, NULL, NULL } +}; -static const DBusObjectPathVTable hs_table = { - .message_function = hs_message, +static DBusSignalVTable headset_signals[] = { + { "Connected", "" }, + { "Disconnected", "" }, + { "AnswerRequested", "" }, + { "Stopped", "" }, + { "Playing", "" }, + { "SpeakerGainChanged", "q" }, + { "MicrophoneGainChanged", "q" }, + { NULL, NULL } }; static struct headset *headset_add_internal(const bdaddr_t *bda) @@ -1628,10 +1620,22 @@ static struct headset *headset_add_internal(const bdaddr_t *bda) snprintf(hs->object_path, sizeof(hs->object_path), HEADSET_PATH_BASE "%d", headset_uid++); - if (!dbus_connection_register_object_path(connection, hs->object_path, - &hs_table, hs)) { + if (!dbus_connection_create_object_path(connection, hs->object_path, + hs, NULL)) { error("D-Bus failed to register %s path", hs->object_path); - free (hs); + g_free(hs); + return NULL; + } + + if (!dbus_connection_register_interface(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_HEADSET_INTERFACE, hs->object_path); + dbus_connection_destroy_object_path(connection, + hs->object_path); + g_free(hs); return NULL; } @@ -1671,14 +1675,14 @@ void headset_remove(const char *path) { struct headset *hs; - if (!dbus_connection_get_object_path_data(connection, path, + if (!dbus_connection_get_object_user_data(connection, path, (void *) &hs)) return; if (hs->state > HEADSET_STATE_DISCONNECTED) - hs_disconnect(hs, NULL); + hs_disconnect(NULL, NULL, hs); - if (!dbus_connection_unregister_object_path(connection, path)) + if (!dbus_connection_destroy_object_path(connection, path)) error("D-Bus failed to unregister %s path", path); headsets = g_slist_remove(headsets, hs); diff --git a/audio/headset.h b/audio/headset.h index e51236ad..ae5119fc 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -27,6 +27,8 @@ #include +#define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" + #define BUF_SIZE 1024 const char *headset_get(const bdaddr_t *bda); diff --git a/audio/manager.c b/audio/manager.c index 04bb3b96..19a02150 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -41,6 +41,7 @@ #include #include "dbus.h" +#include "dbus-helper.h" #include "logging.h" #include "ipc.h" @@ -154,7 +155,8 @@ static void manager_remove_headset(char *path) g_free(path); } -static DBusHandlerResult am_create_headset(DBusMessage *msg) +static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, + void *data) { const char *hs_path; const char *address; @@ -197,7 +199,8 @@ static DBusHandlerResult am_create_headset(DBusMessage *msg) return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_remove_headset(DBusMessage *msg) +static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *msg, + void *data) { DBusError derr; DBusMessage *reply; @@ -250,7 +253,8 @@ static DBusHandlerResult am_remove_headset(DBusMessage *msg) return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_list_headsets(DBusMessage *msg) +static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg, + void *data) { DBusMessageIter iter; DBusMessageIter array_iter; @@ -275,7 +279,8 @@ static DBusHandlerResult am_list_headsets(DBusMessage *msg) return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_get_default_headset(DBusMessage *msg) +static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessage *msg, + void *data) { DBusMessage *reply; @@ -294,7 +299,8 @@ static DBusHandlerResult am_get_default_headset(DBusMessage *msg) return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_change_default_headset(DBusMessage *msg) +static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMessage *msg, + void *data) { DBusError derr; DBusMessage *reply; @@ -331,41 +337,25 @@ static DBusHandlerResult am_change_default_headset(DBusMessage *msg) return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_message(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *interface, *member; - - interface = dbus_message_get_interface(msg); - member = dbus_message_get_member(msg); - - if (!strcmp(DBUS_INTERFACE_INTROSPECTABLE, interface) && - !strcmp("Introspect", member)) - return simple_introspect(conn, msg, data); - - if (strcmp(interface, "org.bluez.audio.Manager") != 0) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (strcmp(member, "CreateHeadset") == 0) - return am_create_headset(msg); - - if (strcmp(member, "RemoveHeadset") == 0) - return am_remove_headset(msg); - - if (strcmp(member, "ListHeadsets") == 0) - return am_list_headsets(msg); - - if (strcmp(member, "DefaultHeadset") == 0) - return am_get_default_headset(msg); - - if (strcmp(member, "ChangeDefaultHeadset") == 0) - return am_change_default_headset(msg); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} +static DBusMethodVTable manager_methods[] = { + { "CreateHeadset", am_create_headset, + "s", "s" }, + { "RemoveHeadset", am_remove_headset, + "s", "" }, + { "ListHeadsets", am_list_headsets, + "", "as" }, + { "DefaultHeadset", am_get_default_headset, + "", "s" }, + { "ChangeDefaultHeadset", am_change_default_headset, + "s", "" }, + { NULL, NULL, NULL, NULL }, +}; -static const DBusObjectPathVTable am_table = { - .message_function = am_message, +static DBusSignalVTable manager_signals[] = { + { "HeadsetCreated", "s" }, + { "HeadsetRemoved", "s" }, + { "DefaultHeadsetChanged", "s" }, + { NULL, NULL } }; int audio_init(DBusConnection *conn) @@ -401,13 +391,25 @@ int audio_init(DBusConnection *conn) g_io_channel_unref(io); - if (!dbus_connection_register_object_path(conn, AUDIO_MANAGER_PATH, - &am_table, NULL)) { + if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, + NULL, NULL)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); close(sk); return -1; } + if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + manager_methods, + manager_signals, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_MANAGER_INTERFACE, AUDIO_MANAGER_PATH); + dbus_connection_destroy_object_path(conn, + AUDIO_MANAGER_PATH); + close(sk); + return -1; + } + connection = dbus_connection_ref(conn); return 0; diff --git a/audio/manager.h b/audio/manager.h index aae34101..6356ab1c 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -28,6 +28,7 @@ #include "headset.h" #define AUDIO_MANAGER_PATH "/org/bluez/audio" +#define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" #define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" -- cgit From 86477ad9060c5456a394af462e17247ee51779ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 May 2007 22:12:20 +0000 Subject: Use dbus_connection_emit_signal for sending signals --- audio/headset.c | 52 +++++++++++++++++++++++----------------------------- audio/manager.c | 54 +++++++++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 54 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 7b043af3..23c1410e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -272,7 +272,6 @@ static gboolean headset_open_input(struct headset *hs, const char *input) static void hs_signal_gain_setting(struct headset *hs, const char *buf) { const char *name; - DBusMessage *signal; dbus_uint16_t gain; if (strlen(buf) < 6) { @@ -292,31 +291,12 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) return; } - signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name); - if (!signal) { - error("Unable to allocate new GainChanged signal"); - return; - } - gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); - dbus_message_append_args(signal, DBUS_TYPE_UINT16, &gain, + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, name, + DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - - send_message_and_unref(connection, signal); -} - -static void hs_signal(struct headset *hs, const char *name) -{ - DBusMessage *signal; - - signal = dbus_message_new_signal(hs->object_path, "org.bluez.audio.Headset", name); - if (!signal) { - error("Unable to allocate new AnswerRequested signal"); - return; - } - - send_message_and_unref(connection, signal); } static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) @@ -354,7 +334,9 @@ static void close_sco(struct headset *hs) headset_close_input(hs); assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Stopped"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, "Stopped", + DBUS_TYPE_INVALID); } @@ -414,7 +396,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->ring_timer = 0; } - hs_signal(hs, "AnswerRequested"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + "AnswerRequested", + DBUS_TYPE_INVALID); break; case HEADSET_EVENT_INVALID: @@ -503,7 +488,10 @@ static void auth_callback(DBusPendingCall *call, void *data) debug("Accepted connection from %s for %s", hs_address, hs->object_path); hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Connected"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + "Connected", + DBUS_TYPE_INVALID); } dbus_message_unref(reply); @@ -649,7 +637,9 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs->pending_connect = NULL; hs->state = HEADSET_STATE_PLAYING; - hs_signal(hs, "Playing"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + "Playing", DBUS_TYPE_INVALID); return FALSE; @@ -701,7 +691,9 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he hs->pending_connect->io = NULL; hs->state = HEADSET_STATE_CONNECTED; - hs_signal(hs, "Connected"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + "Connected", DBUS_TYPE_INVALID); debug("Connected to %s", hs_address); @@ -1197,7 +1189,9 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, ba2str(&hs->bda, hs_address); info("Disconnected from %s, %s", &hs_address, hs->object_path); - hs_signal(hs, "Disconnected"); + dbus_connection_emit_signal(connection, hs->object_path, + AUDIO_HEADSET_INTERFACE, + "Disconnected", DBUS_TYPE_INVALID); hs->data_start = 0; hs->data_length = 0; diff --git a/audio/manager.c b/audio/manager.c index 19a02150..5d381972 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -87,25 +87,6 @@ static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg descr ? descr : "Invalid arguments in method call"); } -static void manager_signal(DBusConnection *conn, const char *name, - const char *param) -{ - DBusMessage *signal; - - signal = dbus_message_new_signal("/org/bluez/audio", - "org.bluez.audio.Manager", - name); - if (!signal) { - error("Unable to create new D-Bus signal"); - return; - } - - dbus_message_append_args(signal, DBUS_TYPE_STRING, ¶m, - DBUS_TYPE_INVALID); - - send_message_and_unref(conn, signal); -} - static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; @@ -141,11 +122,19 @@ void manager_add_headset(const char *path) headsets = g_slist_append(headsets, my_path); - manager_signal(connection, "HeadsetCreated", my_path); + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_INVALID); if (!default_hs) { default_hs = my_path; - manager_signal(connection, "DefaultHeadsetChanged", my_path); + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_INVALID); } } @@ -235,16 +224,27 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms headsets = g_slist_remove(headsets, path); if (default_hs == path) { + const char *param; + if (!headsets) default_hs = NULL; else default_hs = headsets->data; - manager_signal(connection, "DefaultHeadsetChanged", - default_hs ? default_hs : ""); + param = default_hs ? default_hs : ""; + + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, ¶m, + DBUS_TYPE_INVALID); } - manager_signal(connection, "HeadsetRemoved", path); + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetRemoved", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); headset_remove(path); @@ -332,7 +332,11 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes default_hs = match->data; - manager_signal(connection, "DefaultHeadsetChanged", default_hs); + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, &default_hs, + DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } -- cgit From 07958fc3612225ceeffcbbcbae320a2bedcd774d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 10 May 2007 08:45:28 +0000 Subject: Update API descriptions --- audio/audio-api.txt | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 497c6cb6..33dd17c5 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -114,4 +114,3 @@ Signals void AnswerRequested() void MicrophoneGainChanged(uint16 gain) The microphone gain changed. - -- cgit From 84a0c1a2fa19abcdb7afaa4ff597a31d67754442 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 10 May 2007 10:30:24 +0000 Subject: Don't install ALSA plugins without audio service --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 80f62b28..c1bd4ba3 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -14,7 +14,6 @@ bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.h headset. bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -endif if ALSA alsadir = $(libdir)/alsa-lib @@ -29,6 +28,7 @@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ endif +endif AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS@ -- cgit From cd2e0f9256fb31133357f5a0a986f62ee9c6f0df Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 11 May 2007 11:25:21 +0000 Subject: Add missing G_IO_NVAL flags (potential busyloop otherwise) --- audio/headset.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 23c1410e..3a82bcd0 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -631,7 +631,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ if (hs->audio_input) - g_io_add_watch(hs->audio_input, G_IO_IN, audio_input_to_sco_cb, hs); + g_io_add_watch(hs->audio_input, G_IO_IN | G_IO_NVAL, + audio_input_to_sco_cb, hs); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -788,7 +789,8 @@ static int rfcomm_connect(struct headset *hs, int *err) debug("Connect in progress"); - g_io_add_watch(hs->pending_connect->io, G_IO_OUT, (GIOFunc) rfcomm_connect_cb, hs); + g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, + (GIOFunc) rfcomm_connect_cb, hs); } else { debug("Connect succeeded with first try"); rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, hs); @@ -1555,7 +1557,8 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, debug("Connect in progress"); - g_io_add_watch(c->io, G_IO_OUT, (GIOFunc) sco_connect_cb, hs); + g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, + (GIOFunc) sco_connect_cb, hs); } else { debug("Connect succeeded with first try"); sco_connect_cb(c->io, G_IO_OUT, hs); -- cgit From d0553fb41562838ff9ca9bf1c0f6602aa7df4ce0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 11 May 2007 11:32:23 +0000 Subject: Remove unecessary checks --- audio/headset.c | 53 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 3a82bcd0..5d991fb4 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -588,6 +588,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, { int ret, sk, err, flags; socklen_t len; + DBusMessage *reply; if (cond & G_IO_NVAL) return FALSE; @@ -621,13 +622,9 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); - if (hs->pending_connect->msg) { - DBusMessage *reply; - - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(connection, reply); - } + reply = dbus_message_new_method_return(hs->pending_connect->msg); + if (reply) + send_message_and_unref(connection, reply); /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ if (hs->audio_input) @@ -645,13 +642,11 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - if (hs->pending_connect) { - err_connect_failed(connection, hs->pending_connect->msg, err); - if (hs->pending_connect->io) - g_io_channel_close(hs->pending_connect->io); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - } + err_connect_failed(connection, hs->pending_connect->msg, err); + if (hs->pending_connect->io) + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; @@ -701,29 +696,25 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, hs); - if (hs->pending_connect) { - if (hs->pending_connect->msg) { - DBusMessage *reply; - - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(connection, reply); - } + if (hs->pending_connect->msg) { + DBusMessage *reply; - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; + reply = dbus_message_new_method_return(hs->pending_connect->msg); + if (reply) + send_message_and_unref(connection, reply); } + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; + return FALSE; failed: - if (hs->pending_connect) { - err_connect_failed(connection, hs->pending_connect->msg, err); - if (hs->pending_connect->io) - g_io_channel_close(hs->pending_connect->io); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - } + err_connect_failed(connection, hs->pending_connect->msg, err); + if (hs->pending_connect->io) + g_io_channel_close(hs->pending_connect->io); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; -- cgit From 396c3c819743ae85089d358c882b9657554a6452 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 14 May 2007 12:29:09 +0000 Subject: Small cleanup --- audio/audio-api.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 33dd17c5..26b4d46a 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -9,7 +9,6 @@ Copyright (C) 2005-2006 Brad Midgley Audio Manager hierarchy ======================= -Service org.bluez.audio Interface org.bluez.audio.Manager Object path /org/bluez/audio @@ -53,7 +52,6 @@ Signals void HeadsetCreated(string path) Audio Headset hierarchy ======================= -Service org.bluez.audio Interface org.bluez.audio.Headset Object path /org/bluez/audio/headset* @@ -74,7 +72,7 @@ Methods void Connect() Cancel the incoming call indication. void Play() - + Open the audio connection to the headset. void Stop() @@ -86,7 +84,6 @@ Methods void Connect() Returns true if an audio connection to the headset is active. - Signals void AnswerRequested() Sent when the answer button is pressed on the headset @@ -108,7 +105,7 @@ Signals void AnswerRequested() Sent when the audio connection is opened void SpeakerGainChanged(uint16 gain) - + The speaker gain changed. void MicrophoneGainChanged(uint16 gain) -- cgit From 2f9928fa78e9b4c767a5f584d068fb44b43856cd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 15 May 2007 12:45:35 +0000 Subject: Refactoring to allow adding support for more profiles --- audio/audio-api.txt | 36 +++++-- audio/headset.c | 288 +++++++++++++++++++--------------------------------- audio/headset.h | 6 +- audio/main.c | 2 +- audio/manager.c | 282 ++++++++++++++++++++++++++++++++++++++++---------- audio/manager.h | 32 +++++- 6 files changed, 396 insertions(+), 250 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 26b4d46a..1f71c14e 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -2,14 +2,13 @@ Bluetooth audio service API description *************************************** Copyright (C) 2004-2007 Marcel Holtmann -Copyright (C) 2005-2006 Johan Hedberg +Copyright (C) 2005-2007 Johan Hedberg Copyright (C) 2005-2006 Brad Midgley -Audio Manager hierarchy -======================= +org.bluez.audio.Manager interface +================================= -Interface org.bluez.audio.Manager Object path /org/bluez/audio Methods array{string} ListHeadsets() @@ -49,11 +48,20 @@ Signals void HeadsetCreated(string path) Sent when the default headset has changed. -Audio Headset hierarchy -======================= +org.bluez.audio.Device interface +================================ -Interface org.bluez.audio.Headset -Object path /org/bluez/audio/headset* +Object path(s) /org/bluez/audio/device* + +Methods string GetAddress() + + Returns the Bluetooth address of the remote device. + + +org.bluez.audio.Headset interface +================================= + +Object path(s) /org/bluez/audio/device* Methods void Connect() @@ -111,3 +119,15 @@ Signals void AnswerRequested() void MicrophoneGainChanged(uint16 gain) The microphone gain changed. + + +org.bluez.audio.Source interface +================================ + +Object path(s) /org/bluez/audio/device* + + +org.bluez.audio.Sink interface +============================== + +Object path(s) /org/bluez/audio/device* diff --git a/audio/headset.c b/audio/headset.c index 5d991fb4..72fc3bfb 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -67,8 +67,7 @@ typedef enum { } headset_event_t; typedef enum { - HEADSET_STATE_UNAUTHORIZED, - HEADSET_STATE_DISCONNECTED, + HEADSET_STATE_DISCONNECTED = 0, HEADSET_STATE_CONNECT_IN_PROGRESS, HEADSET_STATE_CONNECTED, HEADSET_STATE_PLAY_IN_PROGRESS, @@ -82,9 +81,6 @@ struct pending_connect { }; struct headset { - char object_path[128]; - bdaddr_t bda; - GIOChannel *rfcomm; GIOChannel *sco; @@ -110,8 +106,6 @@ static GIOChannel *hs_server = NULL; static uint32_t hs_record_id = 0; -static GSList *headsets = NULL; - static DBusConnection *connection = NULL; static void pending_connect_free(struct pending_connect *c) @@ -169,13 +163,6 @@ static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) return error_reply(conn, msg, "org.bluez.audio.Error.Failed", "Failed"); } -static gint headset_bda_cmp(gconstpointer headset, gconstpointer bda) -{ - const struct headset *hs = headset; - - return bacmp(&hs->bda, bda); -} - static gboolean headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -269,7 +256,7 @@ static gboolean headset_open_input(struct headset *hs, const char *input) } #endif -static void hs_signal_gain_setting(struct headset *hs, const char *buf) +static void hs_signal_gain_setting(audio_device_t *device, const char *buf) { const char *name; dbus_uint16_t gain; @@ -293,7 +280,7 @@ static void hs_signal_gain_setting(struct headset *hs, const char *buf) gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); @@ -321,8 +308,10 @@ static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_l return HEADSET_EVENT_UNKNOWN; } -static void close_sco(struct headset *hs) +static void close_sco(audio_device_t *device) { + struct headset *hs = device->headset; + g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; @@ -334,15 +323,15 @@ static void close_sco(struct headset *hs) headset_close_input(hs); assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Stopped", DBUS_TYPE_INVALID); } - static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, - struct headset *hs) + audio_device_t *device) { + struct headset *hs = device->headset; unsigned char buf[BUF_SIZE]; char *cr, rsp[BUF_SIZE]; gsize bytes_read = 0; @@ -387,7 +376,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { case HEADSET_EVENT_GAIN: - hs_signal_gain_setting(hs, &hs->buf[hs->data_start] + 2); + hs_signal_gain_setting(device, &hs->buf[hs->data_start] + 2); break; case HEADSET_EVENT_KEYPRESS: @@ -396,7 +385,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->ring_timer = 0; } - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); @@ -432,17 +421,17 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, failed: if (hs->sco) - close_sco(hs); - hs_disconnect(NULL, NULL, hs); + close_sco(device); + hs_disconnect(NULL, NULL, device); return FALSE; } -static void send_cancel_auth(struct headset *hs) +static void send_cancel_auth(audio_device_t *device) { DBusMessage *cancel; char addr[18], *address = addr; - const char *uuid = ""; + const char *uuid = HSP_AG_UUID; cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", @@ -452,7 +441,7 @@ static void send_cancel_auth(struct headset *hs) return; } - ba2str(&hs->bda, addr); + ba2str(&device->bda, addr); dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); @@ -462,7 +451,8 @@ static void send_cancel_auth(struct headset *hs) static void auth_callback(DBusPendingCall *call, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; @@ -471,7 +461,7 @@ static void auth_callback(DBusPendingCall *call, void *data) error("Access denied: %s", err.message); if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - send_cancel_auth(hs); + send_cancel_auth(device); } dbus_error_free(&err); g_io_channel_close(hs->rfcomm); @@ -481,14 +471,15 @@ static void auth_callback(DBusPendingCall *call, void *data) char hs_address[18]; g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + (GIOFunc) rfcomm_io_cb, device); - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); - debug("Accepted connection from %s for %s", hs_address, hs->object_path); + debug("Accepted headset connection from %s for %s", hs_address, + device->object_path); hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -499,7 +490,8 @@ static void auth_callback(DBusPendingCall *call, void *data) static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; @@ -536,7 +528,8 @@ failed: static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; char buf[1024]; gsize bytes_read; gsize bytes_written, total_bytes_written; @@ -579,13 +572,14 @@ static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond disconn: error("Audio connection got disconnected"); if (hs->sco) - close_sco(hs); + close_sco(device); return FALSE; } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - struct headset *hs) + audio_device_t *device) { + struct headset *hs = device->headset; int ret, sk, err, flags; socklen_t len; DBusMessage *reply; @@ -611,7 +605,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } - debug("SCO socket opened for headset %s", hs->object_path); + debug("SCO socket opened for headset %s", device->object_path); hs->sco = chan; hs->pending_connect->io = NULL; @@ -629,13 +623,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ if (hs->audio_input) g_io_add_watch(hs->audio_input, G_IO_IN | G_IO_NVAL, - audio_input_to_sco_cb, hs); + audio_input_to_sco_cb, device); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_PLAYING; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); @@ -654,8 +648,10 @@ failed: return FALSE; } -static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs) +static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, + audio_device_t *device) { + struct headset *hs = device->headset; char hs_address[18]; int sk, ret, err; socklen_t len; @@ -682,19 +678,19 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct he goto failed; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); hs->rfcomm = chan; hs->pending_connect->io = NULL; hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); debug("Connected to %s", hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, hs); + (GIOFunc) rfcomm_io_cb, device); if (hs->pending_connect->msg) { DBusMessage *reply; @@ -721,8 +717,9 @@ failed: return FALSE; } -static int rfcomm_connect(struct headset *hs, int *err) +static int rfcomm_connect(audio_device_t *device, int *err) { + struct headset *hs = device->headset; struct sockaddr_rc addr; char address[18]; int sk; @@ -730,7 +727,7 @@ static int rfcomm_connect(struct headset *hs, int *err) assert(hs != NULL && hs->pending_connect != NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - ba2str(&hs->bda, address); + ba2str(&device->bda, address); debug("Connecting to %s channel %d", address, hs->pending_connect->ch); @@ -761,7 +758,7 @@ static int rfcomm_connect(struct headset *hs, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &hs->bda); + bacpy(&addr.rc_bdaddr, &device->bda); addr.rc_channel = hs->pending_connect->ch; hs->pending_connect->io = g_io_channel_unix_new(sk); @@ -781,10 +778,10 @@ static int rfcomm_connect(struct headset *hs, int *err) debug("Connect in progress"); g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) rfcomm_connect_cb, hs); + (GIOFunc) rfcomm_connect_cb, device); } else { debug("Connect succeeded with first try"); - rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, hs); + rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device); } return 0; @@ -986,7 +983,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct pending_connect *c; assert(hs != NULL && hs->pending_connect && !hs->rfcomm); @@ -1053,7 +1051,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if (rfcomm_connect(hs, &err) < 0) { + if (rfcomm_connect(device, &err) < 0) { error("Unable to connect"); if (c->msg) err_connect_failed(connection, c->msg, err); @@ -1064,7 +1062,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - finish_sdp_transaction(&hs->bda); + finish_sdp_transaction(&device->bda); return; @@ -1081,13 +1079,14 @@ failed: pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; - finish_sdp_transaction(&hs->bda); + finish_sdp_transaction(&device->bda); } static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; if (!hs || !hs->sco) @@ -1109,7 +1108,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_CONNECTED; } - close_sco(hs); + close_sco(device); if (reply) send_message_and_unref(connection, reply); @@ -1120,7 +1119,8 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t playing; @@ -1146,7 +1146,8 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; char hs_address[18]; @@ -1179,10 +1180,10 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_DISCONNECTED; - ba2str(&hs->bda, hs_address); - info("Disconnected from %s, %s", &hs_address, hs->object_path); + ba2str(&device->bda, hs_address); + info("Disconnected from %s, %s", hs_address, device->object_path); - dbus_connection_emit_signal(connection, hs->object_path, + dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); @@ -1198,7 +1199,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t connected; @@ -1226,7 +1228,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct pending_connect *c; char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; @@ -1288,7 +1291,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - ba2str(&hs->bda, address); + ba2str(&device->bda, address); handle = array[0]; @@ -1303,7 +1306,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - dbus_pending_call_set_notify(pending, get_record_reply, hs, NULL); + dbus_pending_call_set_notify(pending, get_record_reply, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); dbus_message_unref(reply); @@ -1321,7 +1324,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { DBusPendingCall *pending; - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; const char *hs_svc = "hsp"; const char *addr_ptr; char hs_address[18]; @@ -1352,7 +1356,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); addr_ptr = hs_address; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_STRING, &hs_svc, @@ -1367,22 +1371,23 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return err_connect_failed(connection, msg, EIO); } - dbus_pending_call_set_notify(pending, get_handles_reply, hs, NULL); + dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); return DBUS_HANDLER_RESULT_HANDLED;; } -static GIOError headset_send_ring(struct headset *hs) +static GIOError headset_send_ring(audio_device_t *device) { + struct headset *hs = device->headset; const char *ring_str = "\r\nRING\r\n"; GIOError err; gsize total_written, written, count; assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { - error("the headset %s is not connected", hs->object_path); + error("the headset %s is not connected", device->object_path); return G_IO_ERROR_UNKNOWN; } @@ -1402,11 +1407,11 @@ static GIOError headset_send_ring(struct headset *hs) static gboolean ring_timer_cb(gpointer data) { - struct headset *hs = data; + audio_device_t *device = data; - assert(hs != NULL); + assert(device != NULL); - if (headset_send_ring(hs) != G_IO_ERROR_NONE) + if (headset_send_ring(device) != G_IO_ERROR_NONE) error("Sending RING failed"); return TRUE; @@ -1415,7 +1420,8 @@ static gboolean ring_timer_cb(gpointer data) static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; assert(hs != NULL); @@ -1434,12 +1440,12 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, goto done; } - if (headset_send_ring(hs) != G_IO_ERROR_NONE) { + if (headset_send_ring(device) != G_IO_ERROR_NONE) { dbus_message_unref(reply); return err_failed(connection, msg); } - hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, hs); + hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); done: if (reply) @@ -1451,7 +1457,8 @@ done: static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) @@ -1481,7 +1488,8 @@ done: static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, void *data) { - struct headset *hs = data; + audio_device_t *device = data; + struct headset *hs = device->headset; struct sockaddr_sco addr; struct pending_connect *c; int sk, err; @@ -1537,7 +1545,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &hs->bda); + bacpy(&addr.sco_bdaddr, &device->bda); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -1549,10 +1557,10 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, debug("Connect in progress"); g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) sco_connect_cb, hs); + (GIOFunc) sco_connect_cb, device); } else { debug("Connect succeeded with first try"); - sco_connect_cb(c->io, G_IO_OUT, hs); + sco_connect_cb(c->io, G_IO_OUT, device); } hs->pending_connect = c; @@ -1589,93 +1597,15 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -static struct headset *headset_add_internal(const bdaddr_t *bda) -{ - static int headset_uid = 0; - struct headset *hs; - GSList *match; - - match = g_slist_find_custom(headsets, bda, headset_bda_cmp); - if (match) - return match->data; - - hs = g_try_new0(struct headset, 1); - if (!hs) { - error("Allocating new hs connection struct failed!"); - return NULL; - } - - snprintf(hs->object_path, sizeof(hs->object_path), - HEADSET_PATH_BASE "%d", headset_uid++); - - if (!dbus_connection_create_object_path(connection, hs->object_path, - hs, NULL)) { - error("D-Bus failed to register %s path", hs->object_path); - g_free(hs); - return NULL; - } - - if (!dbus_connection_register_interface(connection, hs->object_path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) { - error("Failed to register %s interface to %s", - AUDIO_HEADSET_INTERFACE, hs->object_path); - dbus_connection_destroy_object_path(connection, - hs->object_path); - g_free(hs); - return NULL; - } - - bacpy(&hs->bda, bda); - - headsets = g_slist_append(headsets, hs); - - return hs; -} - -const char *headset_add(const bdaddr_t *bda) -{ - struct headset *hs; - - hs = headset_add_internal(bda); - if (!hs) - return NULL; - - return hs->object_path; -} - -const char *headset_get(const bdaddr_t *bda) +headset_t *headset_init(const char *object_path) { - GSList *match; - struct headset *hs; - - match = g_slist_find_custom(headsets, bda, headset_bda_cmp); - if (!match) + if (!dbus_connection_register_interface(connection, object_path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) return NULL; - hs = match->data; - - return hs->object_path; -} - -void headset_remove(const char *path) -{ - struct headset *hs; - - if (!dbus_connection_get_object_user_data(connection, path, - (void *) &hs)) - return; - - if (hs->state > HEADSET_STATE_DISCONNECTED) - hs_disconnect(NULL, NULL, hs); - - if (!dbus_connection_destroy_object_path(connection, path)) - error("D-Bus failed to unregister %s path", path); - - headsets = g_slist_remove(headsets, hs); - - g_free(hs); + return g_new0(headset_t, 1); } static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) @@ -1684,11 +1614,11 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * struct sockaddr_rc addr; socklen_t size; char hs_address[18], *address = hs_address; - const char *uuid = ""; - struct headset *hs = NULL; + const char *uuid = HSP_AG_UUID; + audio_device_t *device; + struct headset *hs; DBusMessage *auth; DBusPendingCall *pending; - GSList *match; if (cond & G_IO_NVAL) return FALSE; @@ -1709,19 +1639,13 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * return TRUE; } - match = g_slist_find_custom(headsets, &addr.rc_bdaddr, headset_bda_cmp); - if (!match) { - hs = headset_add_internal(&addr.rc_bdaddr); - if (!hs) { - error("Unable to create a new headset object"); - close(cli_sk); - return TRUE; - } - - manager_add_headset(hs->object_path); + device = manager_headset_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; } - else - hs = match->data; + + hs = device->headset; if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { debug("Refusing new connection since one already exists"); @@ -1743,7 +1667,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * goto failed; } - ba2str(&hs->bda, hs_address); + ba2str(&device->bda, hs_address); dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); @@ -1753,7 +1677,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * goto failed; } - dbus_pending_call_set_notify(pending, auth_callback, hs, NULL); + dbus_pending_call_set_notify(pending, auth_callback, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(auth); @@ -1820,7 +1744,7 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -int headset_init(DBusConnection *conn) +int headset_server_init(DBusConnection *conn) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; diff --git a/audio/headset.h b/audio/headset.h index ae5119fc..de8e8a38 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -31,13 +31,15 @@ #define BUF_SIZE 1024 +typedef struct headset headset_t; + const char *headset_get(const bdaddr_t *bda); const char *headset_add(const bdaddr_t *bda); -void headset_remove(const char *path); +headset_t *headset_init(const char *path); -int headset_init(DBusConnection *conn); +int headset_server_init(DBusConnection *conn); void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index 2188e6e7..5c44df9b 100644 --- a/audio/main.c +++ b/audio/main.c @@ -78,7 +78,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_init(conn) < 0) { + if (headset_server_init(conn) < 0) { error("Headset initialization failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index 5d381972..682158c2 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -56,9 +56,9 @@ static DBusConnection *connection = NULL; -static char *default_hs = NULL; +static audio_device_t *default_hs = NULL; -static GSList *headsets = NULL; +static GSList *devices = NULL; static int unix_sock = -1; @@ -116,85 +116,242 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } -void manager_add_headset(const char *path) +static audio_device_t *find_device(bdaddr_t *bda) { - char *my_path = g_strdup(path); + GSList *l; + + for (l = devices; l != NULL; l = l->next) { + audio_device_t *device = l->data; + if (bacmp(&device->bda, bda) == 0) + return device; + } + + return NULL; +} + +static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + audio_device_t *device = data; + DBusMessage *reply; + char address[18], *ptr = address; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - headsets = g_slist_append(headsets, my_path); + ba2str(&device->bda, address); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + +static DBusMethodVTable device_methods[] = { + { "GetAddress", device_get_address, "", "s" }, + { NULL, NULL, NULL, NULL } +}; + +static audio_device_t *add_device(bdaddr_t *bda) +{ + static int device_id = 0; + audio_device_t *device; + + device = g_new0(audio_device_t, 1); + + bacpy(&device->bda, bda); + + snprintf(device->object_path, sizeof(device->object_path) - 1, + "%s/device%d", AUDIO_MANAGER_PATH, device_id++); + + if (!dbus_connection_create_object_path(connection, device->object_path, + device, NULL)) { + error("D-Bus failed to register %s path", device->object_path); + g_free(device); + return NULL; + } + + if (!dbus_connection_register_interface(connection, device->object_path, + AUDIO_DEVICE_INTERFACE, + device_methods, NULL, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_DEVICE_INTERFACE, device->object_path); + dbus_connection_destroy_object_path(connection, + device->object_path); + g_free(device); + return NULL; + } + + devices = g_slist_append(devices, device); + + return device; +} + +static void remove_device(audio_device_t *device) +{ + devices = g_slist_remove(devices, device); + dbus_connection_destroy_object_path(connection, device->object_path); + g_free(device->headset); + g_free(device); +} + +audio_device_t *manager_headset_connected(bdaddr_t *bda) +{ + audio_device_t *device; + const char *path; + + device = find_device(bda); + if (device && device->headset) + return device; + + if (!device) + device = add_device(bda); + + if (!device) + return NULL; + + if (!device->headset) + device->headset = headset_init(device->object_path); + + if (!device->headset) + return NULL; + + path = device->object_path; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", - DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); if (!default_hs) { - default_hs = my_path; + default_hs = device; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &my_path, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); } -} -static void manager_remove_headset(char *path) -{ - headset_remove(path); - g_free(path); + return device; } -static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *hs_path; const char *address; bdaddr_t bda; - DBusMessage *reply; + audio_device_t *device; DBusError derr; + DBusMessageIter iter, array_iter; dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } + + str2ba(address, &bda); + + device = find_device(&bda); + if (device) { + const char *iface, *path = device->object_path; + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + "s", &array_iter); + if (device->headset) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(conn, reply); + } + + device = add_device(&bda); + /* + resolve_services(conn, device, msg); + */ + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *path, *address; + bdaddr_t bda; + DBusMessage *reply; + DBusError derr; + audio_device_t *device; + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } + str2ba(address, &bda); + + device = find_device(&bda); + if (!device) + device = add_device(&bda); + + if (!device) + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to create new audio device"); + + device->headset = headset_init(device->object_path); + if (!device->headset) + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to init Headset interface"); + + path = device->object_path; + reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - str2ba(address, &bda); - - hs_path = headset_get(&bda); - if (!hs_path) { - hs_path = headset_add(&bda); - if (!hs_path) - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to create new headset object"); - manager_add_headset(hs_path); - } - dbus_message_append_args(reply, DBUS_TYPE_STRING, &hs_path, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } +static gint device_path_cmp(gconstpointer a, gconstpointer b) +{ + const audio_device_t *device = a; + const char *path = b; + + return strcmp(device->object_path, path); +} + static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *msg, void *data) { DBusError derr; DBusMessage *reply; GSList *match; - char *path; + const char *path; + audio_device_t *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -209,7 +366,7 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); + match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", @@ -219,19 +376,24 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - path = match->data; + device = match->data; - headsets = g_slist_remove(headsets, path); + remove_device(device); - if (default_hs == path) { + if (default_hs == device) { const char *param; + GSList *l; + + default_hs = NULL; - if (!headsets) - default_hs = NULL; - else - default_hs = headsets->data; + for (l = devices; l != NULL; l = l->next) { + device = l->data; - param = default_hs ? default_hs : ""; + if (device->headset) + default_hs = device; + } + + param = default_hs ? default_hs->object_path : ""; dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -246,10 +408,6 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - headset_remove(path); - - g_free(path); - return send_message_and_unref(connection, reply); } @@ -270,9 +428,18 @@ static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); - for (l = headsets; l != NULL; l = l->next) + for (l = devices; l != NULL; l = l->next) { + audio_device_t *device = l->data; + const char *path; + + if (!device->headset) + continue; + + path = device->object_path; + dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &l->data); + DBUS_TYPE_STRING, &path); + } dbus_message_iter_close_container(&iter, &array_iter); @@ -283,6 +450,7 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessag void *data) { DBusMessage *reply; + const char *path; if (!default_hs) return error_reply(connection, msg, @@ -293,7 +461,9 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessag if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_hs, + path = default_hs->object_path; + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); @@ -320,7 +490,7 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes return DBUS_HANDLER_RESULT_HANDLED; } - match = g_slist_find_custom(headsets, path, (GCompareFunc) strcmp); + match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", @@ -332,16 +502,20 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes default_hs = match->data; + path = default_hs->object_path; + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &default_hs, + DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); } static DBusMethodVTable manager_methods[] = { + { "CreateDevice", am_create_device, + "s", "sas" }, { "CreateHeadset", am_create_headset, "s", "s" }, { "RemoveHeadset", am_remove_headset, @@ -425,11 +599,9 @@ void audio_exit(void) unix_sock = -1; - if (headsets) { - g_slist_foreach(headsets, (GFunc) manager_remove_headset, NULL); - g_slist_free(headsets); - headsets = NULL; - } + g_slist_foreach(devices, (GFunc) remove_device, NULL); + g_slist_free(devices); + devices = NULL; dbus_connection_unref(connection); diff --git a/audio/manager.h b/audio/manager.h index 6356ab1c..9fd98d65 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -30,9 +30,37 @@ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -#define HEADSET_PATH_BASE AUDIO_MANAGER_PATH "/headset" +#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" -void manager_add_headset(const char *path); +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" + +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" + +#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" +#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" + +#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" + +#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" +#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" + +#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" +#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" + +typedef struct audio_device { + char object_path[128]; + bdaddr_t bda; + + headset_t *headset; +/* + sink_t *sink; + source_t *source; + control_t *control; +*/ +} audio_device_t; + +audio_device_t *manager_headset_connected(bdaddr_t *bda); int audio_init(DBusConnection *conn); -- cgit From 6023817d9880a2d71516416b010555f041ec187d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 15 May 2007 12:50:13 +0000 Subject: Remove unused functions and defines from the header file --- audio/headset.c | 2 ++ audio/headset.h | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 72fc3bfb..edd59b4b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -59,6 +59,8 @@ #define RING_INTERVAL 3000 +#define BUF_SIZE 1024 + typedef enum { HEADSET_EVENT_KEYPRESS, HEADSET_EVENT_GAIN, diff --git a/audio/headset.h b/audio/headset.h index de8e8a38..b06f1c60 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -29,14 +29,8 @@ #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" -#define BUF_SIZE 1024 - typedef struct headset headset_t; -const char *headset_get(const bdaddr_t *bda); - -const char *headset_add(const bdaddr_t *bda); - headset_t *headset_init(const char *path); int headset_server_init(DBusConnection *conn); -- cgit From d342f2a2b1457a25c50cb661afb56f10595b925c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 May 2007 11:03:50 +0000 Subject: Add place holders for AudioGW and Target interface support --- audio/manager.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/manager.h b/audio/manager.h index 9fd98d65..b23a4bae 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -54,9 +54,11 @@ typedef struct audio_device { headset_t *headset; /* + audio_gw_t *audio_gw; sink_t *sink; source_t *source; control_t *control; + target_t *target; */ } audio_device_t; -- cgit From b0df182cfe740cfa79418d5f9ecd9511e625c0c8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 17 May 2007 23:44:17 +0000 Subject: Add a couple of method proposals inspired by Havoc's recent blog post on D-Bus API design, parcularly the minimizing roundtrips aspect. --- audio/audio-api.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 1f71c14e..083f56d0 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -11,7 +11,21 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio -Methods array{string} ListHeadsets() +Methods + array{string, array{string}} ListDevices() + + Retuns an array of elements that consist of a string + indicating the object path of a device as well as a + string list that corresponds to the set of interfaces + that the object implements. + + array{string, array{string}} GetConnectedDevices() + + Returns an array of elements of the same format as + ListDevices() for each device and interface which + is in a connected state. + + array{string} ListHeadsets() Returns list of headset objects that are configured. -- cgit From 8ffaf19faa86e2c8065f0188b985399c3f3e4e70 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 09:04:48 +0000 Subject: Add some more necessary methods and signals --- audio/audio-api.txt | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 083f56d0..9121dd75 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -12,6 +12,23 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio Methods + string, array{string} CreateDevice(string address) + + Creates a new audio device object. If not yet done, + this method will perform a SDP query on the remote + device and return first when the query is complete, + so be sure to call this method asynchronously. + + The first return parameter is the object path of the + newly created object. The second one is a list of + available interfaces (i.e. the set of audio profiles + which it implements). + + void RemoveDevice(string path) + + Removes a device from the device tree. If there are + any connections open to the device they will be closed. + array{string, array{string}} ListDevices() Retuns an array of elements that consist of a string @@ -49,7 +66,16 @@ Methods Removes a headset object and all information related to it. -Signals void HeadsetCreated(string path) +Signals + void DeviceCreated(string path, array{string} interfaces) + + Sent when a new device object has been created. + + void DeviceRemoved(string path) + + Sent when a device object has been removed. + + void HeadsetCreated(string path) Sent when a new headset object has been created. -- cgit From 2bf938fe553681b5cc8d35ff0d6b30ae9c3d3d97 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 09:15:54 +0000 Subject: Add place holders for not yet implemented methods and signals --- audio/manager.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 682158c2..2d4d22a0 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -287,6 +287,27 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult am_remove_device(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_list_devices(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult am_connected_devices(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -515,21 +536,29 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes static DBusMethodVTable manager_methods[] = { { "CreateDevice", am_create_device, - "s", "sas" }, + "s", "sas" }, + { "RemoveDevice", am_remove_device, + "s", "" }, + { "ListDevices", am_list_devices, + "", "a(sas)" }, + { "GetConnectedDevices", am_connected_devices, + "", "a(sas)" }, { "CreateHeadset", am_create_headset, - "s", "s" }, + "s", "s" }, { "RemoveHeadset", am_remove_headset, - "s", "" }, + "s", "" }, { "ListHeadsets", am_list_headsets, - "", "as" }, + "", "as" }, { "DefaultHeadset", am_get_default_headset, - "", "s" }, + "", "s" }, { "ChangeDefaultHeadset", am_change_default_headset, "s", "" }, { NULL, NULL, NULL, NULL }, }; static DBusSignalVTable manager_signals[] = { + { "DeviceCreated", "sas" }, + { "DeviceRemoved", "s" }, { "HeadsetCreated", "s" }, { "HeadsetRemoved", "s" }, { "DefaultHeadsetChanged", "s" }, -- cgit From 5c3cff06eb6cfd03f5133a7d087c55413875e400 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 16:32:56 +0000 Subject: Simplify API parameter lists (with the drawback of causing some extra round-trips) --- audio/audio-api.txt | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 9121dd75..d6e66b9f 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -12,35 +12,25 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio Methods - string, array{string} CreateDevice(string address) + string CreateDevice(string address) Creates a new audio device object. If not yet done, this method will perform a SDP query on the remote device and return first when the query is complete, so be sure to call this method asynchronously. - The first return parameter is the object path of the - newly created object. The second one is a list of - available interfaces (i.e. the set of audio profiles - which it implements). + The return parameter is the object path of the newly + created object. - void RemoveDevice(string path) + void RemoveDevice(string path) Removes a device from the device tree. If there are any connections open to the device they will be closed. - array{string, array{string}} ListDevices() + array{string} ListDevices() - Retuns an array of elements that consist of a string - indicating the object path of a device as well as a - string list that corresponds to the set of interfaces - that the object implements. - - array{string, array{string}} GetConnectedDevices() - - Returns an array of elements of the same format as - ListDevices() for each device and interface which - is in a connected state. + Retuns an array of strings indicating the object paths + of available devices array{string} ListHeadsets() @@ -67,7 +57,7 @@ Methods related to it. Signals - void DeviceCreated(string path, array{string} interfaces) + void DeviceCreated(string path) Sent when a new device object has been created. @@ -97,6 +87,11 @@ Methods string GetAddress() Returns the Bluetooth address of the remote device. + array{string} GetConnectedInterfaces() + + Returns a string list of interfaces that are in a + connected state. + org.bluez.audio.Headset interface ================================= -- cgit From fb1612c37310e09e8503a113c0114c0ba9584c99 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 18:26:56 +0000 Subject: Update code to match more closely to API description --- audio/headset.c | 16 +++++++++------- audio/headset.h | 2 ++ audio/manager.c | 49 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 47 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index edd59b4b..dbebf67c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1202,20 +1202,14 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { audio_device_t *device = data; - struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t connected; - assert(hs); - reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->state >= HEADSET_STATE_CONNECTED) - connected = TRUE; - else - connected = FALSE; + connected = headset_is_connected(device->headset); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); @@ -1746,6 +1740,14 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } +gboolean headset_is_connected(headset_t *headset) +{ + if (headset->state >= HEADSET_STATE_CONNECTED) + return TRUE; + else + return FALSE; +} + int headset_server_init(DBusConnection *conn) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; diff --git a/audio/headset.h b/audio/headset.h index b06f1c60..06ee8483 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -33,6 +33,8 @@ typedef struct headset headset_t; headset_t *headset_init(const char *path); +gboolean headset_is_connected(headset_t *headset); + int headset_server_init(DBusConnection *conn); void headset_exit(void); diff --git a/audio/manager.c b/audio/manager.c index 2d4d22a0..d2f89529 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -148,8 +148,40 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *m return send_message_and_unref(conn, reply); } +static DBusHandlerResult device_get_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + DBusMessageIter iter, array_iter; + audio_device_t *device = data; + DBusMessage *reply; + const char *iface; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + if (device->headset && headset_is_connected(device->headset)) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(conn, reply); +} + static DBusMethodVTable device_methods[] = { - { "GetAddress", device_get_address, "", "s" }, + { "GetAddress", device_get_address, + "", "s" }, + { "GetConnectedInterfaces", device_get_connected, + "", "s" }, { NULL, NULL, NULL, NULL } }; @@ -301,13 +333,6 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static DBusHandlerResult am_connected_devices(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -536,13 +561,11 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes static DBusMethodVTable manager_methods[] = { { "CreateDevice", am_create_device, - "s", "sas" }, + "s", "s" }, { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, - "", "a(sas)" }, - { "GetConnectedDevices", am_connected_devices, - "", "a(sas)" }, + "", "as" }, { "CreateHeadset", am_create_headset, "s", "s" }, { "RemoveHeadset", am_remove_headset, @@ -557,7 +580,7 @@ static DBusMethodVTable manager_methods[] = { }; static DBusSignalVTable manager_signals[] = { - { "DeviceCreated", "sas" }, + { "DeviceCreated", "s" }, { "DeviceRemoved", "s" }, { "HeadsetCreated", "s" }, { "HeadsetRemoved", "s" }, -- cgit From 9ac6da7a56723334bf172f56fedb6aee6e38fc6c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 19:09:46 +0000 Subject: Mark GetConnectedInterfaces as experimental --- audio/audio-api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index d6e66b9f..b25766ad 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -87,7 +87,7 @@ Methods string GetAddress() Returns the Bluetooth address of the remote device. - array{string} GetConnectedInterfaces() + array{string} GetConnectedInterfaces() [experimental] Returns a string list of interfaces that are in a connected state. -- cgit From 491deff56459cd63650a998fd47f648214b8e94e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 19:10:08 +0000 Subject: Implement RemoveDevice and ListDevices --- audio/manager.c | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index d2f89529..b4eab0bc 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -319,18 +319,36 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult am_remove_device(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - static DBusHandlerResult am_list_devices(DBusConnection *conn, DBusMessage *msg, void *data) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + DBusMessageIter iter, array_iter; + DBusMessage *reply; + GSList *l; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + for (l = devices; l != NULL; l = l->next) { + audio_device_t *device = l->data; + const char *path; + + path = device->object_path; + + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &path); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(connection, reply); } static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, @@ -390,7 +408,8 @@ static gint device_path_cmp(gconstpointer a, gconstpointer b) return strcmp(device->object_path, path); } -static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_remove_device(DBusConnection *conn, + DBusMessage *msg, void *data) { DBusError derr; @@ -457,6 +476,13 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, DBusMessage *ms return send_message_and_unref(connection, reply); } +static DBusHandlerResult am_remove_headset(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return am_remove_device(conn, msg, data); +} + static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg, void *data) { -- cgit From b8695c1e9809e1da1a9ab4a6f155f76a2e2cda55 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 19:13:13 +0000 Subject: Emit DeviceRemoved signal when a device gets removed --- audio/manager.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index b4eab0bc..b746484e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -473,6 +473,12 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceRemoved", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + return send_message_and_unref(connection, reply); } -- cgit From 3568bdd8c1a21ba36e9baf0b1485a1960d1c566b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 18 May 2007 19:19:35 +0000 Subject: Send HeadsetCreated signal when calling CreateHeadset --- audio/manager.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index b746484e..00114f14 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -393,6 +393,11 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); -- cgit From 723fde49c5c473d01a739b116cb674623ed5a40e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 May 2007 01:18:39 +0000 Subject: Implement CreateDevice, associated SDP discovery and other necessary changes --- audio/headset.c | 152 ++++++---------- audio/headset.h | 4 +- audio/manager.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++++++----- audio/manager.h | 14 ++ 4 files changed, 554 insertions(+), 140 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index dbebf67c..b83bd3fe 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -77,12 +77,13 @@ typedef enum { } headset_state_t; struct pending_connect { - int ch; DBusMessage *msg; GIOChannel *io; }; struct headset { + int rfcomm_ch; + GIOChannel *rfcomm; GIOChannel *sco; @@ -119,52 +120,6 @@ static void pending_connect_free(struct pending_connect *c) g_free(c); } -static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *descr) -{ - DBusMessage *derr; - - if (!conn || !msg) - return DBUS_HANDLER_RESULT_HANDLED; - - derr = dbus_message_new_error(msg, name, descr); - if (!derr) { - error("Unable to allocate new error return"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - return send_message_and_unref(conn, derr); -} - -static DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.AlreadyConnected", - "Already connected to a device"); -} - -static DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.NotConnected", - "Not connected to any device"); -} - -static DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.NotSupported", - "The service is not supported by the remote device"); -} - -static DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *msg, int err) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.ConnectFailed", - strerror(err)); -} - -static DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.Failed", "Failed"); -} - static gboolean headset_close_output(struct headset *hs) { assert(hs != NULL); @@ -661,9 +616,10 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; - assert(hs != NULL && hs->pending_connect != NULL && - hs->rfcomm == NULL && - hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); + assert(hs != NULL); + assert(hs->pending_connect != NULL); + assert(hs->rfcomm == NULL); + assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); sk = g_io_channel_unix_get_fd(chan); @@ -731,7 +687,7 @@ static int rfcomm_connect(audio_device_t *device, int *err) ba2str(&device->bda, address); - debug("Connecting to %s channel %d", address, hs->pending_connect->ch); + debug("Connecting to %s channel %d", address, hs->rfcomm_ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { @@ -761,7 +717,7 @@ static int rfcomm_connect(audio_device_t *device, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, &device->bda); - addr.rc_channel = hs->pending_connect->ch; + addr.rc_channel = hs->rfcomm_ch; hs->pending_connect->io = g_io_channel_unix_new(sk); if (!hs->pending_connect->io) { @@ -941,47 +897,12 @@ int headset_remove_ag_record(uint32_t rec_id) return 0; } -static void finish_sdp_transaction(bdaddr_t *dba) -{ - char address[18], *addr_ptr = address; - DBusMessage *msg, *reply; - DBusError derr; - - ba2str(dba, address); - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", - "org.bluez.Adapter", - "FinishRemoteServiceTransaction"); - if (!msg) { - error("Unable to allocate new method call"); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { - error("FinishRemoteServiceTransaction(%s) failed: %s", - address, derr.message); - dbus_error_free(&derr); - return; - } - - dbus_message_unref(reply); -} - static void get_record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; DBusError derr; uint8_t *array; - int array_len, record_len, err = EIO; + int array_len, record_len, err = EIO, ch = -1; sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; @@ -1042,17 +963,19 @@ static void get_record_reply(DBusPendingCall *call, void *data) } if (!sdp_get_access_protos(record, &protos)) { - c->ch = sdp_get_proto_port(protos, RFCOMM_UUID); + ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); protos = NULL; } - if (c->ch == -1) { + if (ch == -1) { error("Unable to extract RFCOMM channel from service record"); goto failed_not_supported; } + hs->rfcomm_ch = ch; + if (rfcomm_connect(device, &err) < 0) { error("Unable to connect"); if (c->msg) @@ -1064,7 +987,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - finish_sdp_transaction(&device->bda); + finish_sdp_transaction(connection, &device->bda); return; @@ -1081,7 +1004,7 @@ failed: pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; - finish_sdp_transaction(&device->bda); + finish_sdp_transaction(connection, &device->bda); } static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, @@ -1325,6 +1248,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, const char *hs_svc = "hsp"; const char *addr_ptr; char hs_address[18]; + int err; assert(hs != NULL); @@ -1341,6 +1265,17 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL; + if (hs->rfcomm_ch > 0) { + if (rfcomm_connect(device, &err) < 0) { + error("Unable to connect"); + pending_connect_free(hs->pending_connect); + hs->pending_connect = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; + return err_connect_failed(conn, msg, err); + } else + return DBUS_HANDLER_RESULT_HANDLED; + } + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", "GetRemoteServiceHandles"); @@ -1438,7 +1373,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, if (headset_send_ring(device) != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(connection, msg); + return err_failed(connection, msg, "Failed"); } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); @@ -1593,15 +1528,42 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -headset_t *headset_init(const char *object_path) +headset_t *headset_init(const char *object_path, sdp_record_t *record) { + int ch; + sdp_list_t *protos; + headset_t *headset; + if (!dbus_connection_register_interface(connection, object_path, AUDIO_HEADSET_INTERFACE, headset_methods, headset_signals, NULL)) return NULL; - return g_new0(headset_t, 1); + headset = g_new0(headset_t, 1); + + headset->rfcomm_ch = -1; + + if (!record) + return headset; + + if (sdp_get_access_protos(record, &protos) < 0) { + error("Unable to get access protos from headset record"); + return headset; + } + + ch = sdp_get_proto_port(protos, RFCOMM_UUID); + + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); + sdp_list_free(protos, NULL); + + if (ch > 0) { + headset->rfcomm_ch = ch; + debug("Discovered Headset service on RFCOMM channel %d", ch); + } else + error("Unable to get RFCOMM channel from Headset record"); + + return headset; } static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) diff --git a/audio/headset.h b/audio/headset.h index 06ee8483..3ccbf8b2 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -24,6 +24,8 @@ #define __AUDIO_HEADSET_H #include +#include +#include #include @@ -31,7 +33,7 @@ typedef struct headset headset_t; -headset_t *headset_init(const char *path); +headset_t *headset_init(const char *object_path, sdp_record_t *record); gboolean headset_is_connected(headset_t *headset); diff --git a/audio/manager.c b/audio/manager.c index 00114f14..5f7f5f88 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -26,6 +26,7 @@ #endif #include +#include #include #include #include @@ -35,6 +36,8 @@ #include #include +#include +#include #include @@ -54,6 +57,25 @@ #define SOCKET_NAME "/org/bluez/audio" +typedef enum { + GENERIC_AUDIO = 0, + ADVANCED_AUDIO, + AV_REMOTE, + GET_RECORDS +} audio_sdp_state_t; + +struct audio_sdp_data { + audio_device_t *device; + + DBusConnection *conn; + DBusMessage *msg; + + GSList *handles; + GSList *records; + + audio_sdp_state_t state; +}; + static DBusConnection *connection = NULL; static audio_device_t *default_hs = NULL; @@ -62,6 +84,10 @@ static GSList *devices = NULL; static int unix_sock = -1; +static void get_next_record(struct audio_sdp_data *data); +static DBusHandlerResult get_handles(const char *uuid, + struct audio_sdp_data *data); + /* FIXME: Remove these once global error functions exist */ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, const char *name, const char *descr) @@ -80,13 +106,44 @@ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, return send_message_and_unref(conn, derr); } -static DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, const char *descr) { return error_reply(conn, msg, "org.bluez.audio.Error.InvalidArguments", descr ? descr : "Invalid arguments in method call"); } +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.AlreadyConnected", + "Already connected to a device"); +} + +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.NotConnected", + "Not connected to any device"); +} + +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.NotSupported", + "The service is not supported by the remote device"); +} + +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, int err) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.ConnectFailed", + strerror(err)); +} + +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.Failed", dsc); +} + static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; @@ -185,7 +242,12 @@ static DBusMethodVTable device_methods[] = { { NULL, NULL, NULL, NULL } }; -static audio_device_t *add_device(bdaddr_t *bda) +static void free_device(audio_device_t *device) +{ + g_free(device); +} + +static audio_device_t *create_device(bdaddr_t *bda) { static int device_id = 0; audio_device_t *device; @@ -197,11 +259,24 @@ static audio_device_t *add_device(bdaddr_t *bda) snprintf(device->object_path, sizeof(device->object_path) - 1, "%s/device%d", AUDIO_MANAGER_PATH, device_id++); + return device; +} + +static void remove_device(audio_device_t *device) +{ + devices = g_slist_remove(devices, device); + dbus_connection_destroy_object_path(connection, device->object_path); + g_free(device->headset); + g_free(device); +} + + +static gboolean add_device(audio_device_t *device) +{ if (!dbus_connection_create_object_path(connection, device->object_path, device, NULL)) { error("D-Bus failed to register %s path", device->object_path); - g_free(device); - return NULL; + return FALSE; } if (!dbus_connection_register_interface(connection, device->object_path, @@ -211,21 +286,390 @@ static audio_device_t *add_device(bdaddr_t *bda) AUDIO_DEVICE_INTERFACE, device->object_path); dbus_connection_destroy_object_path(connection, device->object_path); - g_free(device); - return NULL; + return FALSE; } devices = g_slist_append(devices, device); - return device; + return TRUE; } -static void remove_device(audio_device_t *device) +void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) { - devices = g_slist_remove(devices, device); - dbus_connection_destroy_object_path(connection, device->object_path); - g_free(device->headset); - g_free(device); + char address[18], *addr_ptr = address; + DBusMessage *msg, *reply; + DBusError derr; + + ba2str(dba, address); + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "FinishRemoteServiceTransaction"); + if (!msg) { + error("Unable to allocate new method call"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, + &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { + error("FinishRemoteServiceTransaction(%s) failed: %s", + address, derr.message); + dbus_error_free(&derr); + return; + } + + dbus_message_unref(reply); +} + +static void handle_record(sdp_record_t *record, audio_device_t *device) +{ + sdp_list_t *classes; + uuid_t uuid; + uint16_t uuid16; + + if (sdp_get_service_classes(record, &classes) < 0) { + error("Unable to get service classes from record"); + return; + } + + memcpy(&uuid, classes->data, sizeof(uuid)); + + if (!sdp_uuid128_to_uuid(&uuid)) { + error("Not a 16 bit UUID"); + goto done; + } + + if (uuid.type == SDP_UUID32) { + if (uuid.value.uuid32 > 0xFFFF) { + error("Not a 16 bit UUID"); + goto done; + } + uuid16 = (uint16_t) uuid.value.uuid32; + } else + uuid16 = uuid.value.uuid16; + + switch (uuid16) { + case HEADSET_SVCLASS_ID: + debug("Found Headset record"); + if (device->headset) + debug("Multiple Headset records found"); + else + device->headset = headset_init(device->object_path, + record); + break; + case HEADSET_AGW_SVCLASS_ID: + debug("Found Headset AG record"); + break; + case HANDSFREE_SVCLASS_ID: + debug("Found Hansfree record"); + break; + case HANDSFREE_AGW_SVCLASS_ID: + debug("Found Handsfree AG record"); + break; + case AUDIO_SINK_SVCLASS_ID: + debug("Found Audio Sink"); + break; + case AUDIO_SOURCE_SVCLASS_ID: + debug("Found Audio Source"); + break; + case AV_REMOTE_SVCLASS_ID: + debug("Found AV Remote"); + break; + case AV_REMOTE_TARGET_SVCLASS_ID: + debug("Found AV Target"); + break; + default: + debug("Unrecognized UUID: 0x%04X", uuid16); + break; + } + +done: + sdp_list_free(classes, free); +} + +static void finish_sdp(struct audio_sdp_data *data, gboolean success) +{ + const char *path; + DBusMessage *reply; + + debug("Audio service discovery completed with %s", + success ? "success" : "failure"); + + finish_sdp_transaction(data->conn, &data->device->bda); + + if (!success) + goto done; + + reply = dbus_message_new_method_return(data->msg); + if (!reply) { + success = FALSE; + err_failed(data->conn, data->msg, "Out of memory"); + goto done; + } + + path = data->device->object_path; + + add_device(data->device); + g_slist_foreach(data->records, (GFunc) handle_record, data->device); + + dbus_connection_emit_signal(data->conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + send_message_and_unref(data->conn, reply); + +done: + if (!success) + free_device(data->device); + dbus_connection_unref(data->conn); + dbus_message_unref(data->msg); + g_slist_free(data->handles); + g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL); + g_slist_free(data->records); + g_free(data); +} + +static void get_record_reply(DBusPendingCall *call, + struct audio_sdp_data *data) +{ + DBusMessage *reply; + DBusError derr; + uint8_t *array; + int array_len, record_len; + sdp_record_t *record; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("GetRemoteServiceRecord failed: %s", derr.message); + if (dbus_error_has_name(&derr, + "org.bluez.Error.ConnectionAttemptFailed")) + err_connect_failed(data->conn, data->msg, EHOSTDOWN); + else + err_failed(data->conn, data->msg, derr.message); + dbus_error_free(&derr); + goto failed; + } + + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, + DBUS_TYPE_INVALID)) { + err_failed(data->conn, data->msg, + "Unable to get args from GetRecordReply"); + goto failed; + } + + record = sdp_extract_pdu(array, &record_len); + if (!record) { + error("Unable to extract service record from reply"); + goto done; + } + + if (record_len != array_len) + debug("warning: array len (%d) != record len (%d)", + array_len, record_len); + + data->records = g_slist_append(data->records, record); + +done: + dbus_message_unref(reply); + + if (data->handles) + get_next_record(data); + else + finish_sdp(data, TRUE); + + return; + +failed: + if (reply) + dbus_message_unref(reply); + finish_sdp(data, FALSE); +} + +static void get_next_record(struct audio_sdp_data *data) +{ + DBusMessage *msg; + DBusPendingCall *pending; + char address[18], *ptr = address; + dbus_uint32_t handle; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "GetRemoteServiceRecord"); + if (!msg) { + error("Unable to allocate new method call"); + err_connect_failed(data->conn, data->msg, ENOMEM); + finish_sdp(data, FALSE); + return; + } + + handle = (dbus_uint32_t) data->handles->data; + + data->handles = g_slist_remove(data->handles, data->handles->data); + + ba2str(&data->device->bda, address); + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(data->conn, msg, &pending, -1)) { + error("Sending GetRemoteServiceRecord failed"); + err_connect_failed(data->conn, data->msg, EIO); + finish_sdp(data, FALSE); + return; + } + + dbus_pending_call_set_notify(pending, + (DBusPendingCallNotifyFunction) get_record_reply, + data, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(msg); +} + +static GSList *find_handle(GSList *handles, dbus_uint32_t handle) +{ + while (handles) { + if ((dbus_uint32_t) handles->data == handle) + return handles; + handles = handles->next; + } + + return NULL; +} + +static void get_handles_reply(DBusPendingCall *call, + struct audio_sdp_data *data) +{ + DBusMessage *reply; + DBusError derr; + dbus_uint32_t *array = NULL; + int array_len, i; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("GetRemoteServiceHandles failed: %s", derr.message); + if (dbus_error_has_name(&derr, + "org.bluez.Error.ConnectionAttemptFailed")) + err_connect_failed(data->conn, data->msg, EHOSTDOWN); + else + err_failed(data->conn, data->msg, derr.message); + dbus_error_free(&derr); + goto failed; + } + + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, + DBUS_TYPE_INVALID)) { + + err_failed(data->conn, data->msg, + "Unable to get args from reply"); + goto failed; + } + + for (i = 0; i < array_len; i++) { + if (!find_handle(data->handles, array[i])) + data->handles = g_slist_append(data->handles, + (gpointer) array[i]); + } + + data->state++; + + switch (data->state) { + case ADVANCED_AUDIO: + get_handles(ADVANCED_AUDIO_UUID, data); + break; + case AV_REMOTE: + get_handles(AVRCP_REMOTE_UUID, data); + break; + default: + if (data->handles) + get_next_record(data); + else + finish_sdp(data, TRUE); + } + + dbus_message_unref(reply); + + return; + +failed: + dbus_message_unref(reply); + finish_sdp(data, FALSE); +} + +static DBusHandlerResult get_handles(const char *uuid, + struct audio_sdp_data *data) +{ + DBusPendingCall *pending; + char address[18]; + const char *ptr = address; + DBusMessage *msg; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + "org.bluez.Adapter", + "GetRemoteServiceHandles"); + if (!msg) { + err_failed(data->conn, data->msg, + "Could not create a new dbus message"); + goto failed; + } + + ba2str(&data->device->bda, address); + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + err_failed(data->conn, data->msg, + "Sending GetRemoteServiceHandles failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, + (DBusPendingCallNotifyFunction) get_handles_reply, + data, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(msg); + + return DBUS_HANDLER_RESULT_HANDLED; + +failed: + if (msg) + dbus_message_unref(msg); + finish_sdp(data, FALSE); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult resolve_services(DBusConnection *conn, + DBusMessage *msg, + audio_device_t *device) +{ + struct audio_sdp_data *sdp_data; + + sdp_data = g_new0(struct audio_sdp_data, 1); + sdp_data->msg = dbus_message_ref(msg); + sdp_data->conn = dbus_connection_ref(conn); + sdp_data->device = device; + + return get_handles(GENERIC_AUDIO_UUID, sdp_data); } audio_device_t *manager_headset_connected(bdaddr_t *bda) @@ -237,14 +681,16 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) if (device && device->headset) return device; - if (!device) - device = add_device(bda); - - if (!device) - return NULL; + if (!device) { + device = create_device(bda); + if (!add_device(device)) { + free_device(device); + return NULL; + } + } if (!device->headset) - device->headset = headset_init(device->object_path); + device->headset = headset_init(device->object_path, NULL); if (!device->headset) return NULL; @@ -276,7 +722,6 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg bdaddr_t bda; audio_device_t *device; DBusError derr; - DBusMessageIter iter, array_iter; dbus_error_init(&derr); dbus_message_get_args(msg, &derr, @@ -292,31 +737,18 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg device = find_device(&bda); if (device) { - const char *iface, *path = device->object_path; + const char *path = device->object_path; DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - "s", &array_iter); - if (device->headset) { - iface = AUDIO_HEADSET_INTERFACE; - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &iface); - } - - dbus_message_iter_close_container(&iter, &array_iter); - + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply); } - device = add_device(&bda); - /* - resolve_services(conn, device, msg); - */ + device = create_device(&bda); - return DBUS_HANDLER_RESULT_HANDLED; + return resolve_services(conn, msg, device); } static DBusHandlerResult am_list_devices(DBusConnection *conn, @@ -373,19 +805,23 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms str2ba(address, &bda); device = find_device(&bda); - if (!device) - device = add_device(&bda); - - if (!device) - return error_reply(connection, msg, + if (!device) { + device = create_device(&bda); + if (!add_device(device)) { + free_device(device); + return error_reply(connection, msg, "org.bluez.audio.Error.Failed", "Unable to create new audio device"); + } + } - device->headset = headset_init(device->object_path); - if (!device->headset) + device->headset = headset_init(device->object_path, NULL); + if (!device->headset) { + remove_device(device); return error_reply(connection, msg, "org.bluez.audio.Error.Failed", "Unable to init Headset interface"); + } path = device->object_path; diff --git a/audio/manager.h b/audio/manager.h index b23a4bae..45c5fa5f 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -67,3 +67,17 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda); int audio_init(DBusConnection *conn); void audio_exit(void); + +void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba); + + +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr); +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, int err); +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc); + -- cgit From b4cd9d1c7d90aafb50a9f00247afa842477e08ee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 May 2007 11:02:55 +0000 Subject: Allocate memory for each handle in the list instead of casting between ints and pointers --- audio/manager.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 5f7f5f88..d71509bf 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -435,6 +435,7 @@ done: free_device(data->device); dbus_connection_unref(data->conn); dbus_message_unref(data->msg); + g_slist_foreach(data->handles, (GFunc) g_free, NULL); g_slist_free(data->handles); g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL); g_slist_free(data->records); @@ -505,7 +506,7 @@ static void get_next_record(struct audio_sdp_data *data) DBusMessage *msg; DBusPendingCall *pending; char address[18], *ptr = address; - dbus_uint32_t handle; + dbus_uint32_t *handle; msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", "org.bluez.Adapter", @@ -517,16 +518,18 @@ static void get_next_record(struct audio_sdp_data *data) return; } - handle = (dbus_uint32_t) data->handles->data; + handle = data->handles->data; data->handles = g_slist_remove(data->handles, data->handles->data); ba2str(&data->device->bda, address); dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_UINT32, handle, DBUS_TYPE_INVALID); + g_free(handle); + if (!dbus_connection_send_with_reply(data->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); err_connect_failed(data->conn, data->msg, EIO); @@ -544,7 +547,7 @@ static void get_next_record(struct audio_sdp_data *data) static GSList *find_handle(GSList *handles, dbus_uint32_t handle) { while (handles) { - if ((dbus_uint32_t) handles->data == handle) + if (*(dbus_uint32_t *) handles->data == handle) return handles; handles = handles->next; } @@ -584,9 +587,11 @@ static void get_handles_reply(DBusPendingCall *call, } for (i = 0; i < array_len; i++) { - if (!find_handle(data->handles, array[i])) - data->handles = g_slist_append(data->handles, - (gpointer) array[i]); + if (!find_handle(data->handles, array[i])) { + dbus_uint32_t *handle = g_new(dbus_uint32_t, 1); + *handle = array[i]; + data->handles = g_slist_append(data->handles, handle); + } } data->state++; -- cgit From 242c84eea51f2c9e9f1c5bb190a9126fc5991df4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 19 May 2007 19:21:28 +0000 Subject: Add missing DeviceCreated signal --- audio/manager.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index d71509bf..b7766915 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -681,6 +681,7 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) { audio_device_t *device; const char *path; + gboolean created = FALSE; device = find_device(bda); if (device && device->headset) @@ -692,6 +693,7 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) free_device(device); return NULL; } + created = TRUE; } if (!device->headset) @@ -702,6 +704,13 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) path = device->object_path; + if (created) + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", -- cgit From 29bd3e0dae62eda32dee555199294d8ae2538f0e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 24 May 2007 08:47:30 +0000 Subject: Get rid of unused audio reading & writing functionality (all of this should be done on the alsa side) --- audio/headset.c | 192 ++------------------------------------------------------ 1 file changed, 4 insertions(+), 188 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b83bd3fe..4c2b8423 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -87,11 +87,6 @@ struct headset { GIOChannel *rfcomm; GIOChannel *sco; - char *input; - GIOChannel *audio_input; - char *output; - GIOChannel *audio_output; - guint ring_timer; char buf[BUF_SIZE]; @@ -120,99 +115,6 @@ static void pending_connect_free(struct pending_connect *c) g_free(c); } -static gboolean headset_close_output(struct headset *hs) -{ - assert(hs != NULL); - - if (hs->audio_output == NULL) - return FALSE; - - g_io_channel_close(hs->audio_output); - g_io_channel_unref(hs->audio_output); - hs->audio_output = NULL; - - return TRUE; -} - -/* FIXME: in the furture, that would be great to provide user space alsa driver (not plugin) */ -static gboolean headset_open_output(struct headset *hs, const char *output) -{ - int out; - - assert(hs != NULL && output != NULL); - - headset_close_output(hs); - if (output && hs->output) { - g_free(hs->output); - hs->output = g_strdup(output); - } - - assert(hs->output); - - out = open(hs->output, O_WRONLY | O_SYNC | O_CREAT); - - if (out < 0) { - error("open(%s): %s %d", hs->output, strerror(errno), errno); - return FALSE; - } - - hs->audio_output = g_io_channel_unix_new(out); - if (!hs->audio_output) { - error("Allocating new channel for audio output!"); - return FALSE; - } - - g_io_channel_set_close_on_unref(hs->audio_output, TRUE); - - return TRUE; -} - -static gboolean headset_close_input(struct headset *hs) -{ - assert(hs != NULL); - - if (hs->audio_input == NULL) - return FALSE; - - g_io_channel_close(hs->audio_input); - g_io_channel_unref(hs->audio_input); - hs->audio_input = NULL; - - return TRUE; -} - -#if 0 -static gboolean headset_open_input(struct headset *hs, const char *input) -{ - int in; - - assert(hs != NULL); - - /* we keep the input name, and NULL can be use to reopen */ - if (input && hs->input) { - g_free(hs->input); - hs->input = g_strdup(input); - } - - assert(hs->input); - - in = open(hs->input, O_RDONLY | O_NOCTTY); - - if (in < 0) { - error("open(%s): %s %d", hs->input, strerror(errno), errno); - return FALSE; - } - - hs->audio_input = g_io_channel_unix_new(in); - if (!hs->audio_input) { - error("Allocating new channel for audio input!"); - return FALSE; - } - - return TRUE; -} -#endif - static void hs_signal_gain_setting(audio_device_t *device, const char *buf) { const char *name; @@ -272,12 +174,6 @@ static void close_sco(audio_device_t *device) g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; - if (hs->audio_output) { - g_io_channel_unref(hs->audio_output); - hs->audio_output = NULL; - } - if (hs->audio_input) - headset_close_input(hs); assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; dbus_connection_emit_signal(connection, device->object_path, @@ -445,91 +341,18 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean audio_input_to_sco_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - audio_device_t *device = data; struct headset *hs = device->headset; - char buf[1024]; - gsize bytes_read; - gsize bytes_written, total_bytes_written; - GIOError err; if (cond & G_IO_NVAL) return FALSE; - if (cond & (G_IO_HUP | G_IO_ERR)) - goto failed; - - err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); - if (err != G_IO_ERROR_NONE) - goto failed; - - total_bytes_written = bytes_written = 0; - err = G_IO_ERROR_NONE; - - while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) { - /* FIXME: make it async */ - err = g_io_channel_write(hs->sco, buf + total_bytes_written, - bytes_read - total_bytes_written, &bytes_written); - if (err != G_IO_ERROR_NONE) - error("Error while writting to the audio output channel"); - total_bytes_written += bytes_written; - }; - - return TRUE; - -failed: - headset_close_input(hs); - return FALSE; -} - -static gboolean sco_input_to_audio_output_cb(GIOChannel *chan, GIOCondition cond, gpointer data) -{ - audio_device_t *device = data; - struct headset *hs = device->headset; - char buf[1024]; - gsize bytes_read; - gsize bytes_written, total_bytes_written; - GIOError err; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) - goto disconn; - - if (!hs->audio_output && hs->output) - headset_open_output(hs, hs->output); - - err = g_io_channel_read(chan, buf, sizeof(buf), &bytes_read); - - if (err != G_IO_ERROR_NONE) - goto disconn; - - if (!hs->audio_output) { - error("got %d bytes audio but have nowhere to write it", bytes_read); - return TRUE; - } - - total_bytes_written = bytes_written = 0; - err = G_IO_ERROR_NONE; - - while (err == G_IO_ERROR_NONE && total_bytes_written < bytes_read) { - /* FIXME: make it async */ - err = g_io_channel_write(hs->audio_output, buf + total_bytes_written, - bytes_read - total_bytes_written, &bytes_written); - if (err != G_IO_ERROR_NONE) { - error("Error while writting to the audio output channel"); - } - total_bytes_written += bytes_written; - }; - - return TRUE; - -disconn: error("Audio connection got disconnected"); + if (hs->sco) close_sco(device); + return FALSE; } @@ -568,20 +391,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs->pending_connect->io = NULL; flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; - if (hs->audio_output) - flags |= G_IO_IN; - g_io_add_watch(hs->sco, flags, sco_input_to_audio_output_cb, hs); + g_io_add_watch(hs->sco, flags, (GIOFunc) sco_cb, device); reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) send_message_and_unref(connection, reply); - /* FIXME: do we allow both? pull & push model at the same time on sco && audio_input? */ - if (hs->audio_input) - g_io_add_watch(hs->audio_input, G_IO_IN | G_IO_NVAL, - audio_input_to_sco_cb, device); - pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; -- cgit From 0097d93ac82733bae644a0be3c6ad0eab6cff4db Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 26 May 2007 16:31:17 +0000 Subject: Add unix.c and unix.h skeletons --- audio/Makefile.am | 3 ++- audio/unix.c | 28 ++++++++++++++++++++++++++++ audio/unix.h | 22 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 audio/unix.c create mode 100644 audio/unix.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index c1bd4ba3..63448a86 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -10,7 +10,8 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio -bluetoothd_service_audio_SOURCES = main.c manager.h manager.c headset.h headset.c ipc.h +bluetoothd_service_audio_SOURCES = main.c \ + manager.h manager.c headset.h headset.c ipc.h unix.h unix.c bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ diff --git a/audio/unix.c b/audio/unix.c new file mode 100644 index 00000000..e61680b2 --- /dev/null +++ b/audio/unix.c @@ -0,0 +1,28 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "unix.h" diff --git a/audio/unix.h b/audio/unix.h new file mode 100644 index 00000000..e87dd676 --- /dev/null +++ b/audio/unix.h @@ -0,0 +1,22 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ -- cgit From d2d76421ad539e2c6da87e4b4fd2a42493053f49 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 26 May 2007 17:17:34 +0000 Subject: Move unix socket functionality into unix.c --- audio/ipc.h | 2 ++ audio/main.c | 8 +++++ audio/manager.c | 77 ----------------------------------------------- audio/unix.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/unix.h | 4 +++ 5 files changed, 106 insertions(+), 77 deletions(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 5b545844..8de85531 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -25,6 +25,8 @@ #define IPC_TYPE_CONNECT 0x0001 +#define IPC_SOCKET_NAME "/org/bluez/audio" + struct ipc_hdr { uint16_t id; uint16_t type; diff --git a/audio/main.c b/audio/main.c index 5c44df9b..98dc1d4f 100644 --- a/audio/main.c +++ b/audio/main.c @@ -36,6 +36,7 @@ #include "dbus.h" #include "logging.h" +#include "unix.h" #include "manager.h" #include "headset.h" @@ -73,6 +74,11 @@ int main(int argc, char *argv[]) exit(1); } + if (unix_init() < 0) { + error("Unable to setup unix socket"); + exit(1); + } + if (audio_init(conn) < 0) { error("Audio init failed!"); exit(1); @@ -92,6 +98,8 @@ int main(int argc, char *argv[]) headset_exit(); + unix_exit(); + dbus_connection_unref(conn); g_main_loop_unref(main_loop); diff --git a/audio/manager.c b/audio/manager.c index b7766915..c15d2f5d 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include @@ -47,16 +45,9 @@ #include "dbus-helper.h" #include "logging.h" -#include "ipc.h" #include "headset.h" #include "manager.h" -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - -#define SOCKET_NAME "/org/bluez/audio" - typedef enum { GENERIC_AUDIO = 0, ADVANCED_AUDIO, @@ -82,8 +73,6 @@ static audio_device_t *default_hs = NULL; static GSList *devices = NULL; -static int unix_sock = -1; - static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, struct audio_sdp_data *data); @@ -144,35 +133,6 @@ DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, return error_reply(conn, msg, "org.bluez.audio.Error.Failed", dsc); } -static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) -{ - struct sockaddr_un addr; - socklen_t addrlen; - unsigned char buf[128]; - int sk, len; - - debug("chan %p cond %td data %p", chan, cond, data); - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - g_io_channel_close(chan); - return FALSE; - } - - sk = g_io_channel_unix_get_fd(chan); - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - - len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen); - - debug("path %s len %d", addr.sun_path + 1, len); - - return TRUE; -} - static audio_device_t *find_device(bdaddr_t *bda) { GSList *l; @@ -1077,41 +1037,9 @@ static DBusSignalVTable manager_signals[] = { int audio_init(DBusConnection *conn) { - GIOChannel *io; - struct sockaddr_un addr; - int sk; - - sk = socket(PF_LOCAL, SOCK_DGRAM, 0); - if (sk < 0) { - error("Can't create unix socket: %s (%d)", strerror(errno), errno); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("Can't bind unix socket: %s (%d)", strerror(errno), errno); - close(sk); - return -1; - } - - set_nonblocking(sk); - - unix_sock = sk; - - io = g_io_channel_unix_new(sk); - - g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - unix_event, NULL); - - g_io_channel_unref(io); - if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, NULL, NULL)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - close(sk); return -1; } @@ -1123,7 +1051,6 @@ int audio_init(DBusConnection *conn) AUDIO_MANAGER_INTERFACE, AUDIO_MANAGER_PATH); dbus_connection_destroy_object_path(conn, AUDIO_MANAGER_PATH); - close(sk); return -1; } @@ -1134,10 +1061,6 @@ int audio_init(DBusConnection *conn) void audio_exit(void) { - close(unix_sock); - - unix_sock = -1; - g_slist_foreach(devices, (GFunc) remove_device, NULL); g_slist_free(devices); devices = NULL; diff --git a/audio/unix.c b/audio/unix.c index e61680b2..4f99e5e4 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -25,4 +25,96 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "logging.h" +#include "dbus.h" + +#include "ipc.h" #include "unix.h" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +static int unix_sock = -1; + +static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct sockaddr_un addr; + socklen_t addrlen; + unsigned char buf[128]; + int sk, len; + + debug("chan %p cond %td data %p", chan, cond, data); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + g_io_channel_close(chan); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + + len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen); + + debug("path %s len %d", addr.sun_path + 1, len); + + return TRUE; +} + +int unix_init(void) +{ + GIOChannel *io; + struct sockaddr_un addr; + int sk; + + sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (sk < 0) { + error("Can't create unix socket: %s (%d)", strerror(errno), errno); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("Can't bind unix socket: %s (%d)", strerror(errno), errno); + close(sk); + return -1; + } + + set_nonblocking(sk); + + unix_sock = sk; + + io = g_io_channel_unix_new(sk); + + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + unix_event, NULL); + + g_io_channel_unref(io); + + return 0; +} + +void unix_exit(void) +{ + close(unix_sock); + unix_sock = -1; +} diff --git a/audio/unix.h b/audio/unix.h index e87dd676..4f3fc994 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -20,3 +20,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ + +int unix_init(void); + +void unix_exit(void); -- cgit From e343f2b21a2209d5713e20ad38bbcc862897380b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 26 May 2007 21:12:20 +0000 Subject: Move more common stuff to ipc.h --- audio/ctl_bluetooth.c | 11 ++++------- audio/ipc.h | 6 +++++- audio/pcm_bluetooth.c | 11 +++-------- audio/unix.c | 4 ---- 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index bcc37659..4d73c788 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -31,14 +31,10 @@ #include #include -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif +#include "ipc.h" #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) -#define SOCKET_NAME "/org/bluez/audio" - #define BLUETOOTH_MINVOL 0 #define BLUETOOTH_MAXVOL 15 @@ -220,7 +216,8 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id); + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", + IPC_SOCKET_NAME, id); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't bind socket"); @@ -230,7 +227,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't connect socket"); diff --git a/audio/ipc.h b/audio/ipc.h index 8de85531..038ec4d9 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -25,7 +25,11 @@ #define IPC_TYPE_CONNECT 0x0001 -#define IPC_SOCKET_NAME "/org/bluez/audio" +#define IPC_SOCKET_NAME "\0/org/bluez/audio" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif struct ipc_hdr { uint16_t id; diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d3e457ae..838dcd39 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -33,14 +33,8 @@ #include "ipc.h" -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) -#define SOCKET_NAME "/org/bluez/audio" - struct bluetooth_data { snd_pcm_ioplug_t io; snd_pcm_sframes_t hw_ptr; @@ -198,7 +192,8 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", SOCKET_NAME, id); + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", + IPC_SOCKET_NAME, id); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't bind socket"); @@ -208,7 +203,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", SOCKET_NAME); + snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't connect socket"); diff --git a/audio/unix.c b/audio/unix.c index 4f99e5e4..ce2041b2 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -42,10 +42,6 @@ #include "ipc.h" #include "unix.h" -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 -#endif - static int unix_sock = -1; static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) -- cgit From 0dd153779d22cbce13c03b56b848fac8203b42d7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 26 May 2007 22:18:10 +0000 Subject: Remove initial nul from the socket name define since the snprintf's take care of not touching the first byte of the socket name --- audio/ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 038ec4d9..119aa7b3 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -25,7 +25,7 @@ #define IPC_TYPE_CONNECT 0x0001 -#define IPC_SOCKET_NAME "\0/org/bluez/audio" +#define IPC_SOCKET_NAME "/org/bluez/audio" #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 -- cgit From eb5c0bd71f2e1c427ce197f9c94ab9540d305922 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 28 May 2007 10:32:05 +0000 Subject: Use global D-Bus connection for SDP transaction --- audio/manager.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index c15d2f5d..bafc7d9d 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -58,7 +58,6 @@ typedef enum { struct audio_sdp_data { audio_device_t *device; - DBusConnection *conn; DBusMessage *msg; GSList *handles; @@ -363,7 +362,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) debug("Audio service discovery completed with %s", success ? "success" : "failure"); - finish_sdp_transaction(data->conn, &data->device->bda); + finish_sdp_transaction(connection, &data->device->bda); if (!success) goto done; @@ -371,7 +370,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; - err_failed(data->conn, data->msg, "Out of memory"); + err_failed(connection, data->msg, "Out of memory"); goto done; } @@ -380,7 +379,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) add_device(data->device); g_slist_foreach(data->records, (GFunc) handle_record, data->device); - dbus_connection_emit_signal(data->conn, AUDIO_MANAGER_PATH, + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", DBUS_TYPE_STRING, &path, @@ -388,12 +387,11 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - send_message_and_unref(data->conn, reply); + send_message_and_unref(connection, reply); done: if (!success) free_device(data->device); - dbus_connection_unref(data->conn); dbus_message_unref(data->msg); g_slist_foreach(data->handles, (GFunc) g_free, NULL); g_slist_free(data->handles); @@ -418,9 +416,9 @@ static void get_record_reply(DBusPendingCall *call, error("GetRemoteServiceRecord failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(data->conn, data->msg, EHOSTDOWN); + err_connect_failed(connection, data->msg, EHOSTDOWN); else - err_failed(data->conn, data->msg, derr.message); + err_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto failed; } @@ -428,7 +426,7 @@ static void get_record_reply(DBusPendingCall *call, if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, DBUS_TYPE_INVALID)) { - err_failed(data->conn, data->msg, + err_failed(connection, data->msg, "Unable to get args from GetRecordReply"); goto failed; } @@ -473,7 +471,7 @@ static void get_next_record(struct audio_sdp_data *data) "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - err_connect_failed(data->conn, data->msg, ENOMEM); + err_connect_failed(connection, data->msg, ENOMEM); finish_sdp(data, FALSE); return; } @@ -490,9 +488,9 @@ static void get_next_record(struct audio_sdp_data *data) g_free(handle); - if (!dbus_connection_send_with_reply(data->conn, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); - err_connect_failed(data->conn, data->msg, EIO); + err_connect_failed(connection, data->msg, EIO); finish_sdp(data, FALSE); return; } @@ -530,9 +528,9 @@ static void get_handles_reply(DBusPendingCall *call, error("GetRemoteServiceHandles failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(data->conn, data->msg, EHOSTDOWN); + err_connect_failed(connection, data->msg, EHOSTDOWN); else - err_failed(data->conn, data->msg, derr.message); + err_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto failed; } @@ -541,7 +539,7 @@ static void get_handles_reply(DBusPendingCall *call, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { - err_failed(data->conn, data->msg, + err_failed(connection, data->msg, "Unable to get args from reply"); goto failed; } @@ -591,7 +589,7 @@ static DBusHandlerResult get_handles(const char *uuid, "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { - err_failed(data->conn, data->msg, + err_failed(connection, data->msg, "Could not create a new dbus message"); goto failed; } @@ -603,7 +601,7 @@ static DBusHandlerResult get_handles(const char *uuid, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { - err_failed(data->conn, data->msg, + err_failed(connection, data->msg, "Sending GetRemoteServiceHandles failed"); goto failed; } @@ -623,15 +621,14 @@ failed: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult resolve_services(DBusConnection *conn, - DBusMessage *msg, +static DBusHandlerResult resolve_services(DBusMessage *msg, audio_device_t *device) { struct audio_sdp_data *sdp_data; sdp_data = g_new0(struct audio_sdp_data, 1); - sdp_data->msg = dbus_message_ref(msg); - sdp_data->conn = dbus_connection_ref(conn); + if (msg) + sdp_data->msg = dbus_message_ref(msg); sdp_data->device = device; return get_handles(GENERIC_AUDIO_UUID, sdp_data); @@ -722,7 +719,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg device = create_device(&bda); - return resolve_services(conn, msg, device); + return resolve_services(msg, device); } static DBusHandlerResult am_list_devices(DBusConnection *conn, -- cgit From 11f21abbc245e090345f21734a7a00cdde087078 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 May 2007 08:40:21 +0000 Subject: Update API descriptions to match current plan --- audio/audio-api.txt | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index b25766ad..76751941 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -12,7 +12,7 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio Methods - string CreateDevice(string address) + string CreateDevice(string address, array{string} interfaces) Creates a new audio device object. If not yet done, this method will perform a SDP query on the remote @@ -20,27 +20,28 @@ Methods so be sure to call this method asynchronously. The return parameter is the object path of the newly - created object. + created object. The method will fail if the remote + device does not as a minimum implement the interfaces + indicated by the interfaces parameter. void RemoveDevice(string path) Removes a device from the device tree. If there are any connections open to the device they will be closed. - array{string} ListDevices() + array{string} ListDevices(array{string} interfaces) Retuns an array of strings indicating the object paths - of available devices + of available devices which implement as a minimum the + interfaces given through the parameter. array{string} ListHeadsets() - Returns list of headset objects that - are configured. + Returns list of headset objects that are configured. string DefaultHeadset() - Returns the object path for the default - headset device. + Returns the object path for the default headset device. void ChangeDefaultHeadset(string path) @@ -48,8 +49,8 @@ Methods string CreateHeadset(string address) - Create a new headset device and returns - its object path on return. + Create a new headset device and returns its object path + on return. void RemoveHeadset(string path) -- cgit From 04efa14f48878cd1c1a31afc106f782fc97277a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 09:23:17 +0000 Subject: Bring the audio code up-to-date with the current API spec --- audio/headset.c | 291 +++++++++++++++++++++++++++++++++++++++++++++++++------- audio/headset.h | 9 +- audio/main.c | 2 +- audio/manager.c | 291 ++++++++++++++++++++++++++++++++++++++++++++------------ audio/manager.h | 18 +++- 5 files changed, 506 insertions(+), 105 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 4c2b8423..d9e26459 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -56,6 +56,7 @@ #include "headset.h" #define DEFAULT_HS_AG_CHANNEL 12 +#define DEFAULT_HF_AG_CHANNEL 13 #define RING_INTERVAL 3000 @@ -76,12 +77,20 @@ typedef enum { HEADSET_STATE_PLAYING, } headset_state_t; +typedef enum { + SVC_HEADSET, + SVC_HANDSFREE +} hs_type; + struct pending_connect { DBusMessage *msg; GIOChannel *io; }; struct headset { + uint32_t hsp_handle; + uint32_t hfp_handle; + int rfcomm_ch; GIOChannel *rfcomm; @@ -93,6 +102,8 @@ struct headset { int data_start; int data_length; + hs_type type; + headset_state_t state; struct pending_connect *pending_connect; }; @@ -100,9 +111,13 @@ struct headset { static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data); +static gboolean disable_hfp = FALSE; + static GIOChannel *hs_server = NULL; +static GIOChannel *hf_server = NULL; static uint32_t hs_record_id = 0; +static uint32_t hf_record_id = 0; static DBusConnection *connection = NULL; @@ -184,7 +199,7 @@ static void close_sco(audio_device_t *device) static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; unsigned char buf[BUF_SIZE]; char *cr, rsp[BUF_SIZE]; gsize bytes_read = 0; @@ -195,6 +210,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; + hs = device->headset; + if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; @@ -343,11 +360,13 @@ static void auth_callback(DBusPendingCall *call, void *data) static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; if (cond & G_IO_NVAL) return FALSE; + hs = device->headset; + error("Audio connection got disconnected"); if (hs->sco) @@ -501,6 +520,8 @@ static int rfcomm_connect(audio_device_t *device, int *err) assert(hs != NULL && hs->pending_connect != NULL && hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); + hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + ba2str(&device->bda, address); debug("Connecting to %s channel %d", address, hs->rfcomm_ch); @@ -567,7 +588,7 @@ failed: return -1; } -static int create_ag_record(sdp_buf_t *buf, uint8_t ch) +static int create_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; @@ -607,7 +628,76 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) aproto = sdp_list_append(0, apseq); sdp_set_access_protos(&record, aproto); - sdp_set_info_attr(&record, "Headset", 0, 0); + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 create_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0105; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); if (sdp_gen_record_pdu(&record, buf) < 0) ret = -1; @@ -628,12 +718,11 @@ static int create_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -static uint32_t headset_add_ag_record(uint8_t channel) +static uint32_t headset_add_ag_record(uint8_t channel, sdp_buf_t *buf) { DBusMessage *msg, *reply; DBusError derr; dbus_uint32_t rec_id; - sdp_buf_t buf; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "AddServiceRecord"); @@ -642,20 +731,13 @@ static uint32_t headset_add_ag_record(uint8_t channel) return 0; } - if (create_ag_record(&buf, channel) < 0) { - error("Unable to allocate new service record"); - dbus_message_unref(msg); - return 0; - } - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf.data, buf.data_size, DBUS_TYPE_INVALID); + &buf->data, buf->data_size, DBUS_TYPE_INVALID); dbus_error_init(&derr); reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &derr); - free(buf.data); dbus_message_unref(msg); if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { @@ -901,7 +983,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, } if (hs->state > HEADSET_STATE_CONNECTED) - hs_stop(NULL, NULL, hs); + hs_stop(NULL, NULL, device); if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); @@ -1344,28 +1426,14 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -headset_t *headset_init(const char *object_path, sdp_record_t *record) +static void headset_set_channel(headset_t *headset, sdp_record_t *record) { int ch; sdp_list_t *protos; - headset_t *headset; - - if (!dbus_connection_register_interface(connection, object_path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) - return NULL; - - headset = g_new0(headset_t, 1); - - headset->rfcomm_ch = -1; - - if (!record) - return headset; if (sdp_get_access_protos(record, &protos) < 0) { error("Unable to get access protos from headset record"); - return headset; + return; } ch = sdp_get_proto_port(protos, RFCOMM_UUID); @@ -1378,10 +1446,113 @@ headset_t *headset_init(const char *object_path, sdp_record_t *record) debug("Discovered Headset service on RFCOMM channel %d", ch); } else error("Unable to get RFCOMM channel from Headset record"); +} + +void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc) +{ + switch (svc) { + case HANDSFREE_SVCLASS_ID: + if (disable_hfp) { + debug("Ignoring Handsfree record since HFP support" + " has been disabled"); + return; + } + + if (headset->hfp_handle && + (headset->hfp_handle != record->handle)) { + error("More than one HFP record found on device"); + return; + } + + headset->hfp_handle = record->handle; + break; + + case HEADSET_SVCLASS_ID: + if (headset->hsp_handle && + (headset->hsp_handle != record->handle)) { + error("More than one HSP record found on device"); + return; + } + + headset->hsp_handle = record->handle; + + /* Ignore this record if we already have access to HFP */ + if (headset->hfp_handle) + return; + + break; + + default: + debug("Invalid record passed to headset_update"); + return; + } + + headset_set_channel(headset, record); +} + +headset_t *headset_init(const char *object_path, sdp_record_t *record, + uint16_t svc) +{ + headset_t *headset; + + headset = g_new0(headset_t, 1); + headset->rfcomm_ch = -1; + + if (!record) + goto register_iface; + + switch (svc) { + case HANDSFREE_SVCLASS_ID: + if (disable_hfp) { + debug("Ignoring Handsfree record since HFP support" + " has been disabled"); + g_free(headset); + return NULL; + } + + headset->hfp_handle = record->handle; + break; + + case HEADSET_SVCLASS_ID: + headset->hsp_handle = record->handle; + break; + + default: + debug("Invalid record passed to headset_init"); + g_free(headset); + return NULL; + } + +register_iface: + if (!dbus_connection_register_interface(connection, object_path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) { + g_free(headset); + return NULL; + } + + if (record) + headset_set_channel(headset, record); return headset; } +void headset_free(const char *object_path) +{ + audio_device_t *device; + + if (!dbus_connection_get_object_user_data(connection, object_path, + (void *) &device)) + return; + + if (device->headset->state != HEADSET_STATE_DISCONNECTED) + hs_disconnect(NULL, NULL, device); + + g_free(device->headset); + device->headset = NULL; +} + static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; @@ -1434,6 +1605,11 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * return TRUE; } + if (chan == hs_server) + hs->type = SVC_HEADSET; + else + hs->type = SVC_HANDSFREE; + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "RequestAuthorization"); if (!auth) { @@ -1490,7 +1666,7 @@ static GIOChannel *server_socket(uint8_t *channel) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; + addr.rc_channel = channel ? *channel : 0; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { error("server bind: %s", strerror(errno), errno); @@ -1526,9 +1702,10 @@ gboolean headset_is_connected(headset_t *headset) return FALSE; } -int headset_server_init(DBusConnection *conn) +int headset_server_init(DBusConnection *conn, gboolean no_hfp) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; connection = dbus_connection_ref(conn); @@ -1536,11 +1713,15 @@ int headset_server_init(DBusConnection *conn) if (!hs_server) return -1; - if (!hs_record_id) - hs_record_id = headset_add_ag_record(chan); + if (create_hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + hs_record_id = headset_add_ag_record(chan, &buf); + free(buf.data); if (!hs_record_id) { - error("Unable to register service record"); + error("Unable to register HS AG service record"); g_io_channel_unref(hs_server); hs_server = NULL; return -1; @@ -1549,6 +1730,34 @@ int headset_server_init(DBusConnection *conn) g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) headset_server_io_cb, NULL); + disable_hfp = no_hfp; + + if (disable_hfp) + return 0; + + chan = DEFAULT_HF_AG_CHANNEL; + + hf_server = server_socket(&chan); + if (!hf_server) + return -1; + + if (create_hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hf_record_id = headset_add_ag_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) headset_server_io_cb, NULL); + return 0; } @@ -1564,6 +1773,16 @@ void headset_exit(void) hs_server = NULL; } + if (hf_record_id) { + headset_remove_ag_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } + dbus_connection_unref(connection); connection = NULL; } diff --git a/audio/headset.h b/audio/headset.h index 3ccbf8b2..c02f36a3 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -33,11 +33,16 @@ typedef struct headset headset_t; -headset_t *headset_init(const char *object_path, sdp_record_t *record); +headset_t *headset_init(const char *object_path, sdp_record_t *record, + uint16_t svc); + +void headset_free(const char *object_path); + +void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc); gboolean headset_is_connected(headset_t *headset); -int headset_server_init(DBusConnection *conn); +int headset_server_init(DBusConnection *conn, gboolean disable_hfp); void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index 98dc1d4f..80e3fabf 100644 --- a/audio/main.c +++ b/audio/main.c @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_server_init(conn) < 0) { + if (headset_server_init(conn, FALSE) < 0) { error("Headset initialization failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index bafc7d9d..85d52e86 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,16 @@ #include "headset.h" #include "manager.h" +typedef enum { + HEADSET = 1 << 0, + GATEWAY = 1 << 1, + SINK = 1 << 2, + SOURCE = 1 << 3, + CONTROL = 1 << 4, + TARGET = 1 << 5, + INVALID = 1 << 6 +} audio_service_type; + typedef enum { GENERIC_AUDIO = 0, ADVANCED_AUDIO, @@ -58,10 +69,10 @@ typedef enum { struct audio_sdp_data { audio_device_t *device; - DBusMessage *msg; + DBusMessage *msg; /* Method call or NULL */ - GSList *handles; - GSList *records; + GSList *handles; /* uint32_t * */ + GSList *records; /* sdp_record_t * */ audio_sdp_state_t state; }; @@ -224,8 +235,8 @@ static audio_device_t *create_device(bdaddr_t *bda) static void remove_device(audio_device_t *device) { devices = g_slist_remove(devices, device); + headset_free(device->object_path); dbus_connection_destroy_object_path(connection, device->object_path); - g_free(device->headset); g_free(device); } @@ -253,6 +264,40 @@ static gboolean add_device(audio_device_t *device) return TRUE; } +static uint16_t get_service_uuid(const sdp_record_t *record) +{ + sdp_list_t *classes; + uuid_t uuid; + uint16_t uuid16 = 0; + + if (sdp_get_service_classes(record, &classes) < 0) { + error("Unable to get service classes from record"); + return 0; + } + + memcpy(&uuid, classes->data, sizeof(uuid)); + + if (!sdp_uuid128_to_uuid(&uuid)) { + error("Not a 16 bit UUID"); + sdp_list_free(classes, free); + return 0; + } + + if (uuid.type == SDP_UUID32) { + if (uuid.value.uuid32 > 0xFFFF) { + error("Not a 16 bit UUID"); + goto done; + } + uuid16 = (uint16_t) uuid.value.uuid32; + } else + uuid16 = uuid.value.uuid16; + +done: + sdp_list_free(classes, free); + + return uuid16; +} + void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) { char address[18], *addr_ptr = address; @@ -290,39 +335,18 @@ void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) static void handle_record(sdp_record_t *record, audio_device_t *device) { - sdp_list_t *classes; - uuid_t uuid; uint16_t uuid16; - if (sdp_get_service_classes(record, &classes) < 0) { - error("Unable to get service classes from record"); - return; - } - - memcpy(&uuid, classes->data, sizeof(uuid)); - - if (!sdp_uuid128_to_uuid(&uuid)) { - error("Not a 16 bit UUID"); - goto done; - } - - if (uuid.type == SDP_UUID32) { - if (uuid.value.uuid32 > 0xFFFF) { - error("Not a 16 bit UUID"); - goto done; - } - uuid16 = (uint16_t) uuid.value.uuid32; - } else - uuid16 = uuid.value.uuid16; + uuid16 = get_service_uuid(record); switch (uuid16) { case HEADSET_SVCLASS_ID: debug("Found Headset record"); if (device->headset) - debug("Multiple Headset records found"); + headset_update(device->headset, record, uuid16); else device->headset = headset_init(device->object_path, - record); + record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: debug("Found Headset AG record"); @@ -349,15 +373,45 @@ static void handle_record(sdp_record_t *record, audio_device_t *device) debug("Unrecognized UUID: 0x%04X", uuid16); break; } +} -done: - sdp_list_free(classes, free); +static gint record_iface_cmp(gconstpointer a, gconstpointer b) +{ + const sdp_record_t *record = a; + const char *interface = b; + + switch (get_service_uuid(record)) { + case HEADSET_SVCLASS_ID: + case HANDSFREE_SVCLASS_ID: + return strcmp(interface, AUDIO_HEADSET_INTERFACE); + + case HEADSET_AGW_SVCLASS_ID: + case HANDSFREE_AGW_SVCLASS_ID: + return strcmp(interface, AUDIO_GATEWAY_INTERFACE); + + case AUDIO_SINK_SVCLASS_ID: + return strcmp(interface, AUDIO_SINK_INTERFACE); + + case AUDIO_SOURCE_SVCLASS_ID: + return strcmp(interface, AUDIO_SOURCE_INTERFACE); + + case AV_REMOTE_SVCLASS_ID: + return strcmp(interface, AUDIO_CONTROL_INTERFACE); + + case AV_REMOTE_TARGET_SVCLASS_ID: + return strcmp(interface, AUDIO_TARGET_INTERFACE); + + default: + return -1; + } } static void finish_sdp(struct audio_sdp_data *data, gboolean success) { - const char *path; - DBusMessage *reply; + const char *path, *addr; + char **required; + int required_len, i; + DBusMessage *reply = NULL; debug("Audio service discovery completed with %s", success ? "success" : "failure"); @@ -367,6 +421,36 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (!success) goto done; + if (!data->msg) + goto update; + + if (!dbus_message_get_args(data->msg, NULL, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID)) { + error("Unable to get message args"); + success = FALSE; + goto done; + } + + for (i = 0; i < required_len; i++) { + const char *iface = required[i]; + + if (g_slist_find_custom(data->records, iface, record_iface_cmp)) + continue; + + debug("Required interface %s not supported", iface); + success = FALSE; + err_not_supported(connection, data->msg); + dbus_free_string_array(required); + goto done; + } + + dbus_free_string_array(required); + + path = data->device->object_path; + reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; @@ -374,25 +458,28 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto done; } - path = data->device->object_path; - add_device(data->device); + +update: g_slist_foreach(data->records, (GFunc) handle_record, data->device); - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "DeviceCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (reply) { + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + send_message_and_unref(connection, reply); + } done: if (!success) free_device(data->device); - dbus_message_unref(data->msg); + if (data->msg) + dbus_message_unref(data->msg); g_slist_foreach(data->handles, (GFunc) g_free, NULL); g_slist_free(data->handles); g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL); @@ -654,19 +741,21 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) } if (!device->headset) - device->headset = headset_init(device->object_path, NULL); + device->headset = headset_init(device->object_path, NULL, 0); if (!device->headset) return NULL; path = device->object_path; - if (created) + if (created) { dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); + resolve_services(NULL, device); + } dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -686,17 +775,62 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) return device; } +static gboolean device_supports_interface(audio_device_t *device, + const char *iface) +{ + if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) + return device->headset ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) + return device->gateway ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) + return device->gateway ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) + return device->sink ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) + return device->control ? TRUE : FALSE; + + if (strcmp(iface, AUDIO_TARGET_INTERFACE) == 0) + return device->target ? TRUE : FALSE; + + debug("Unknown interface %s", iface); + + return FALSE; +} + +static gboolean device_matches(audio_device_t *device, char **interfaces) +{ + int i; + + for (i = 0; interfaces[i]; i++) { + if (device_supports_interface(device, interfaces[i])) + continue; + debug("Device does not support interface %s", interfaces[i]); + return FALSE; + } + + return TRUE; +} + static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *address; + const char *address, *path; + char **required; + int required_len; bdaddr_t bda; audio_device_t *device; + DBusMessage *reply; DBusError derr; dbus_error_init(&derr); dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &address, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); @@ -707,19 +841,29 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg str2ba(address, &bda); device = find_device(&bda); - if (device) { - const char *path = device->object_path; - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + if (!device) { + device = create_device(&bda); + dbus_free_string_array(required); + return resolve_services(msg, device); } - device = create_device(&bda); + if (!device_matches(device, required)) { + dbus_free_string_array(required); + return err_not_supported(conn, msg); + } + + dbus_free_string_array(required); + + path = device->object_path; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - return resolve_services(msg, device); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); } static DBusHandlerResult am_list_devices(DBusConnection *conn, @@ -728,7 +872,21 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, { DBusMessageIter iter, array_iter; DBusMessage *reply; + DBusError derr; GSList *l; + char **required; + int required_len; + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { + err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } reply = dbus_message_new_method_return(msg); if (!reply) @@ -743,6 +901,9 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, audio_device_t *device = l->data; const char *path; + if (!device_matches(device, required)) + continue; + path = device->object_path; dbus_message_iter_append_basic(&array_iter, @@ -751,6 +912,8 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, dbus_message_iter_close_container(&iter, &array_iter); + dbus_free_string_array(required); + return send_message_and_unref(connection, reply); } @@ -786,12 +949,14 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms } } - device->headset = headset_init(device->object_path, NULL); if (!device->headset) { - remove_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to init Headset interface"); + device->headset = headset_init(device->object_path, NULL, 0); + if (!device->headset) { + remove_device(device); + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to init Headset interface"); + } } path = device->object_path; @@ -1005,11 +1170,11 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMes static DBusMethodVTable manager_methods[] = { { "CreateDevice", am_create_device, - "s", "s" }, + "sas", "s" }, { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, - "", "as" }, + "as", "as" }, { "CreateHeadset", am_create_headset, "s", "s" }, { "RemoveHeadset", am_remove_headset, diff --git a/audio/manager.h b/audio/manager.h index 45c5fa5f..78ec63ab 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -48,18 +48,30 @@ #define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" #define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" +/* Move these to respective .h files once they exist */ +#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" +#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" +#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" +typedef struct gateway gateway_t; +typedef struct sink sink_t; +typedef struct source source_t; +typedef struct control control_t; +typedef struct target target_t; + typedef struct audio_device { char object_path[128]; bdaddr_t bda; headset_t *headset; -/* - audio_gw_t *audio_gw; + + gateway_t *gateway; sink_t *sink; source_t *source; control_t *control; target_t *target; -*/ + } audio_device_t; audio_device_t *manager_headset_connected(bdaddr_t *bda); -- cgit From 49f901927ea2133526c360c420b532f6b147f0ad Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 09:30:23 +0000 Subject: Disable HFP support for now (since it's incomplete) --- audio/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 80e3fabf..b0eb30c8 100644 --- a/audio/main.c +++ b/audio/main.c @@ -84,7 +84,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_server_init(conn, FALSE) < 0) { + if (headset_server_init(conn, TRUE) < 0) { error("Headset initialization failed!"); exit(1); } -- cgit From a359e4becf3b938c5c0bd263d059b5f354f11c7c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 11:47:29 +0000 Subject: Return error if no audio related services were found in a remote device --- audio/manager.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 85d52e86..b0e91ee9 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -434,6 +434,14 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto done; } + /* Return error if no audio related service records were found */ + if (!data->records) { + debug("No audio audio related service records were found"); + success = FALSE; + err_not_supported(connection, data->msg); + success = FALSE; + } + for (i = 0; i < required_len; i++) { const char *iface = required[i]; -- cgit From 06e91735ba34f0f445badc796eeeb8f52e881281 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 11:48:02 +0000 Subject: Fix typo --- audio/audio-api.txt | 4 ++-- audio/manager.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 76751941..f391415f 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -21,8 +21,8 @@ Methods The return parameter is the object path of the newly created object. The method will fail if the remote - device does not as a minimum implement the interfaces - indicated by the interfaces parameter. + device does not support all of the interfaces listed + in the interfaces parameter. void RemoveDevice(string path) diff --git a/audio/manager.c b/audio/manager.c index b0e91ee9..d4ebdffd 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -439,7 +439,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) debug("No audio audio related service records were found"); success = FALSE; err_not_supported(connection, data->msg); - success = FALSE; + goto done; } for (i = 0; i < required_len; i++) { -- cgit From 814aed96a2c0afa4218cc4384425934fd30faa68 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 12:51:49 +0000 Subject: Add support for configuration file --- audio/Makefile.am | 4 ++-- audio/audio.conf | 27 ++++++++++++++++++++++++++ audio/headset.c | 3 ++- audio/headset.h | 3 ++- audio/main.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 audio/audio.conf (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 63448a86..2285ed7f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -3,7 +3,7 @@ if AUDIOSERVICE if CONFIGFILES confdir = $(sysconfdir)/bluetooth -conf_DATA = audio.service +conf_DATA = audio.service audio.conf endif servicedir = $(libdir)/bluetooth @@ -35,6 +35,6 @@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS INCLUDES = -I$(top_srcdir)/common -EXTRA_DIST = audio.service audio-api.txt asound.conf +EXTRA_DIST = audio.service audio.conf audio-api.txt asound.conf MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/audio.conf b/audio/audio.conf new file mode 100644 index 00000000..f88668e4 --- /dev/null +++ b/audio/audio.conf @@ -0,0 +1,27 @@ +# Configuration file for the audio service + +# This section contains options which are not specific to any +# particular interface +[General] + +# If we want to disable support for specific services +# Defaults to supporting all implemented services +#Disable=Control,Source + +# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA) +# Defaults to HCI +SCORouting=PCM + +# Headset interface specific options (i.e. options which affect how the audio +# service interacts with remote headset devices) +[Headset] + +# Set to true to only support HSP +# Defaults to false +DisableHFP=true + +# Just an example of potential config options for the other interfaces +#[Sink] +#Codecs=SBC,MPEG12 +#SBCChannelMode=joint +#SBCBitpool=51 diff --git a/audio/headset.c b/audio/headset.c index d9e26459..45f989bc 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1702,7 +1702,8 @@ gboolean headset_is_connected(headset_t *headset) return FALSE; } -int headset_server_init(DBusConnection *conn, gboolean no_hfp) +int headset_server_init(DBusConnection *conn, gboolean no_hfp, + gboolean sco_hci) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; sdp_buf_t buf; diff --git a/audio/headset.h b/audio/headset.h index c02f36a3..0325e755 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -42,7 +42,8 @@ void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc); gboolean headset_is_connected(headset_t *headset); -int headset_server_init(DBusConnection *conn, gboolean disable_hfp); +int headset_server_init(DBusConnection *conn, gboolean disable_hfp, + gboolean sco_hci); void headset_exit(void); diff --git a/audio/main.c b/audio/main.c index b0eb30c8..796af3b5 100644 --- a/audio/main.c +++ b/audio/main.c @@ -40,6 +40,10 @@ #include "manager.h" #include "headset.h" +/* Configuration settings */ +static gboolean disable_hfp = FALSE; +static gboolean sco_hci = TRUE; + static GMainLoop *main_loop = NULL; static void sig_term(int sig) @@ -47,6 +51,55 @@ static void sig_term(int sig) g_main_loop_quit(main_loop); } +void read_config(const char *file) +{ + GKeyFile *keyfile; + GError *err = NULL; + gboolean no_hfp; + char *sco_routing; + + keyfile = g_key_file_new(); + + if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { + error("Parsing %s failed: %s", file, err->message); + g_error_free(err); + g_key_file_free(keyfile); + return; + } + + sco_routing = g_key_file_get_string(keyfile, "General", + "SCORouting", &err); + if (err) { + debug("%s: %s", file, err->message); + g_error_free(err); + err = NULL; + } else { + if (strcmp(sco_routing, "PCM") == 0) + sco_hci = FALSE; + else if (strcmp(sco_routing, "HCI") == 0) + sco_hci = TRUE; + else + error("Invalid Headset Routing value: %s", + sco_routing); + g_free(sco_routing); + } + + no_hfp = g_key_file_get_boolean(keyfile, "Headset", + "DisableHFP", &err); + if (err) { + debug("%s: %s", file, err->message); + g_error_free(err); + err = NULL; + } else + disable_hfp = no_hfp; + + debug("Config options: DisableHFP=%s, SCORouting=%s", + disable_hfp ? "true" : "false", + sco_hci ? "HCI" : "PCM"); + + g_key_file_free(keyfile); +} + int main(int argc, char *argv[]) { DBusConnection *conn; @@ -66,6 +119,8 @@ int main(int argc, char *argv[]) enable_debug(); + read_config(CONFIGDIR "/audio.conf"); + main_loop = g_main_loop_new(NULL, FALSE); conn = dbus_bus_system_setup_with_main_loop(NULL, NULL, NULL); @@ -84,7 +139,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_server_init(conn, TRUE) < 0) { + if (headset_server_init(conn, disable_hfp, sco_hci) < 0) { error("Headset initialization failed!"); exit(1); } -- cgit From b3a5bafbf9b521a3c9deabb60c06cf1aa058fe9d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 May 2007 14:09:04 +0000 Subject: Only call headset_free if the device actually has headset support --- audio/manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index d4ebdffd..748f2b71 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -235,7 +235,8 @@ static audio_device_t *create_device(bdaddr_t *bda) static void remove_device(audio_device_t *device) { devices = g_slist_remove(devices, device); - headset_free(device->object_path); + if (device->headset) + headset_free(device->object_path); dbus_connection_destroy_object_path(connection, device->object_path); g_free(device); } -- cgit From 729824372e5d7fd1231c73afea1bea061dd85cc1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 4 Jun 2007 10:30:45 +0000 Subject: Implement Manager.FindDeviceByAddress method --- audio/audio-api.txt | 6 ++++++ audio/manager.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ audio/manager.h | 1 + 3 files changed, 53 insertions(+) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index f391415f..19cefc69 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -39,6 +39,12 @@ Methods Returns list of headset objects that are configured. + array{string} FindDeviceByAddress(string address) + + Searches the list of available devices and returns the + first device which matchess address. If no device is + found returns a DoesNotExist error. + string DefaultHeadset() Returns the object path for the default headset device. diff --git a/audio/manager.c b/audio/manager.c index 748f2b71..1b727f7a 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -137,6 +137,12 @@ DBusHandlerResult err_connect_failed(DBusConnection *conn, strerror(err)); } +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.DoesNotExist", + "Does not exist"); +} + DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc) { @@ -1110,6 +1116,44 @@ static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg return send_message_and_unref(connection, reply); } +static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *path, *address; + DBusMessage *reply; + DBusError derr; + audio_device_t *device; + bdaddr_t bda; + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { + err_invalid_args(connection, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + str2ba(address, &bda); + + device = find_device(&bda); + + if (!device) + return err_does_not_exist(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + path = device->object_path; + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1190,6 +1234,8 @@ static DBusMethodVTable manager_methods[] = { "s", "" }, { "ListHeadsets", am_list_headsets, "", "as" }, + { "FindDeviceByAddress", am_find_by_addr, + "s", "s" }, { "DefaultHeadset", am_get_default_headset, "", "s" }, { "ChangeDefaultHeadset", am_change_default_headset, diff --git a/audio/manager.h b/audio/manager.h index 78ec63ab..45d93d7f 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -90,6 +90,7 @@ DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *msg, int err); +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc); -- cgit From f63a68d621017f6ba41f14136622ce3ff68d6cd3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 4 Jun 2007 10:32:57 +0000 Subject: Fix FindDeviceByAddress API description --- audio/audio-api.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 19cefc69..175f62f3 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -39,11 +39,11 @@ Methods Returns list of headset objects that are configured. - array{string} FindDeviceByAddress(string address) + string FindDeviceByAddress(string address) Searches the list of available devices and returns the - first device which matchess address. If no device is - found returns a DoesNotExist error. + object path of the first device which matchess address. + If no device is found returns a DoesNotExist error. string DefaultHeadset() -- cgit From a551604373fc46741c44524b01353d50852b26f4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 4 Jun 2007 22:03:21 +0000 Subject: Add experiemental code for audio service ipc. --- audio/ipc.h | 74 +++++++++++++++++++++++++++++++++++++++++++-------- audio/pcm_bluetooth.c | 9 +++++++ 2 files changed, 72 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 119aa7b3..e6859a50 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -31,19 +31,71 @@ #define UNIX_PATH_MAX 108 #endif -struct ipc_hdr { - uint16_t id; - uint16_t type; - uint16_t seqnum; - uint16_t length; +/* Supported roles */ +#define PKT_ROLE_NONE 0 +#define PKT_ROLE_AUTO 1 +#define PKT_ROLE_VOICE 2 +#define PKT_ROLE_HIFI 3 + +/* Packet types */ +#define PKT_TYPE_CFG_REQ 0 +#define PKT_TYPE_CFG_RSP 1 +#define PKT_TYPE_STATUS_REQ 3 +#define PKT_TYPE_STATUS_RSP 4 +#define PKT_TYPE_CTL_REQ 5 +#define PKT_TYPE_CTL_RSP 6 + +/* Errors codes */ +#define PKT_ERROR_NONE 0 + +struct ipc_packet { + uint8_t id; /* Device id */ + uint8_t role; /* Audio role eg: voice, wifi, auto... */ + uint8_t type; /* Packet type */ + uint8_t error; /* Packet error code */ + uint8_t length; /* Payload length in bytes */ + uint8_t data[0]; /* Packet payload */ } __attribute__ ((packed)); -struct ipc_connect_cmd { - uint8_t src[6]; - uint8_t dst[6]; - uint16_t uuid; +/* File descriptor options */ +#define CFG_FD_OPT_READ 0 +#define CFG_FD_OPT_WRITE 1 +#define CFG_FD_OPT_READWRITE 2 + +struct ipc_data_cfg { + int fd; /* Stream file descriptor */ + uint8_t fd_opt; /* Stream file descriptor options: read, write or readwrite*/ + uint8_t encoding; /* Stream encoding */ + uint8_t bitpool; /* Encoding bitpool */ + uint8_t channels; /* Number of audio channel */ + uint16_t rate; /* Stream sample rate */ } __attribute__ ((packed)); -struct ipc_connect_evt { - uint16_t id; +/* Device status */ +#define STATUS_DISCONNECTED 0 +#define STATUS_CONNECTING 1 +#define STATUS_CONNECTED 2 +#define STATUS_STREAMING 3 + +struct ipc_data_status { + uint8_t status; /* Stream status */ } __attribute__ ((packed)); + +/* Supported control operations */ +#define DATA_CTL_POWER 0x40 +#define DATA_CTL_VOL_UP 0x41 +#define DATA_CTL_VOL_DOWN 0x42 +#define DATA_CTL_MUTE 0x43 +#define DATA_CTL_PLAY 0x44 +#define DATA_CTL_STOP 0x45 +#define DATA_CTL_PAUSE 0x46 +#define DATA_CTL_RECORD 0x47 +#define DATA_CTL_REWIND 0x48 +#define DATA_CTL_FAST_FORWARD 0x49 +#define DATA_CTL_EJECT 0x4A +#define DATA_CTL_FORWARD 0x4B +#define DATA_CTL_BACKWARD 0x4C + +struct ipc_data_ctl { + uint8_t operation; /* Operation ID */ +} __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 838dcd39..3e473d6c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -182,6 +182,15 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) return -EINVAL; } + if((stream == SND_PCM_STREAM_PLAYBACK) && (opened_for & OPENED_PLAYBACK)) { + SNDERR("Cannot open Bluetooth Headset PCM plugin twice for playback."); + return -EINVAL; + } + if((stream == SND_PCM_STREAM_CAPTURE) && (opened_for & OPENED_CAPTURE)) { + SNDERR("Cannot open Bluetooth Headset PCM plugin twice for capture."); + return -EINVAL; + } + id = abs(getpid() * rand()); sk = socket(PF_LOCAL, SOCK_DGRAM, 0); -- cgit From 6e1999d1ddf223e3a0fc173a8dc6fc2342ab9da3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 4 Jun 2007 22:04:25 +0000 Subject: Revert acidental commit. --- audio/pcm_bluetooth.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 3e473d6c..838dcd39 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -182,15 +182,6 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) return -EINVAL; } - if((stream == SND_PCM_STREAM_PLAYBACK) && (opened_for & OPENED_PLAYBACK)) { - SNDERR("Cannot open Bluetooth Headset PCM plugin twice for playback."); - return -EINVAL; - } - if((stream == SND_PCM_STREAM_CAPTURE) && (opened_for & OPENED_CAPTURE)) { - SNDERR("Cannot open Bluetooth Headset PCM plugin twice for capture."); - return -EINVAL; - } - id = abs(getpid() * rand()); sk = socket(PF_LOCAL, SOCK_DGRAM, 0); -- cgit From 8742ce7c3e92a57cde47481281a4928f8d41a771 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 5 Jun 2007 22:44:36 +0000 Subject: Experiemental code for alsa plugin and ipc. --- audio/ipc.h | 10 +- audio/pcm_bluetooth.c | 317 ++++++++++++++++++++++++++++++++++++++++++-------- audio/unix.c | 34 +++++- 3 files changed, 307 insertions(+), 54 deletions(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index e6859a50..0cd9e620 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -40,10 +40,10 @@ /* Packet types */ #define PKT_TYPE_CFG_REQ 0 #define PKT_TYPE_CFG_RSP 1 -#define PKT_TYPE_STATUS_REQ 3 -#define PKT_TYPE_STATUS_RSP 4 -#define PKT_TYPE_CTL_REQ 5 -#define PKT_TYPE_CTL_RSP 6 +#define PKT_TYPE_STATUS_REQ 2 +#define PKT_TYPE_STATUS_RSP 3 +#define PKT_TYPE_CTL_REQ 4 +#define PKT_TYPE_CTL_RSP 5 /* Errors codes */ #define PKT_ERROR_NONE 0 @@ -68,6 +68,8 @@ struct ipc_data_cfg { uint8_t encoding; /* Stream encoding */ uint8_t bitpool; /* Encoding bitpool */ uint8_t channels; /* Number of audio channel */ + uint8_t pkt_len; /* Stream packet length */ + uint8_t sample_size; /* Sample size in bytes */ uint16_t rate; /* Stream sample rate */ } __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 838dcd39..29583940 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -31,14 +31,28 @@ #include #include +#include +#include + #include "ipc.h" #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) +#ifndef SCO_TXBUFS +#define SCO_TXBUFS 0x03 +#endif + +#ifndef SCO_RXBUFS +#define SCO_RXBUFS 0x04 +#endif + struct bluetooth_data { snd_pcm_ioplug_t io; snd_pcm_sframes_t hw_ptr; - int sock; + struct ipc_data_cfg cfg; /* Bluetooth device config */ + int sock; /* Daemon unix socket */ + uint8_t *buffer; /* Transfer buffer */ + uint8_t count; /* Transfer buffer counter */ }; static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -59,9 +73,9 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - //DBG("io %p", io); + DBG("io %p", io); - //DBG("hw_ptr=%lu", data->hw_ptr); + DBG("hw_ptr=%lu", data->hw_ptr); return data->hw_ptr; } @@ -77,16 +91,171 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) return 0; } + +static int bluetooth_prepare(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + + DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); + + if (io->stream == SND_PCM_STREAM_PLAYBACK) { + /* If not null for playback, xmms doesn't display time correctly */ + data->hw_ptr = 0; + } + else { + /* ALSA library is really picky on the fact hw_ptr is not null. If it is, capture won't start */ + data->hw_ptr = io->period_size; + } + return 0; +} + +static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) +{ + struct bluetooth_data *data = io->private_data; + struct ipc_data_cfg cfg = data->cfg; + uint32_t period_count = io->buffer_size / io->period_size; + + DBG("period_count = %d", period_count); + + if(setsockopt(cfg.fd, SOL_SCO, + io->stream == SND_PCM_STREAM_PLAYBACK ? SCO_TXBUFS : SCO_RXBUFS, + &period_count, + sizeof(period_count)) == 0) { + return 0; + } else if(setsockopt(cfg.fd, SOL_SCO, + io->stream == SND_PCM_STREAM_PLAYBACK ? SO_SNDBUF : SO_RCVBUF, + &period_count, + sizeof(period_count)) == 0) { + return 0; + } else { + SNDERR("Unable to set number of SCO buffers : please upgrade your Kernel !"); + return -EINVAL; + } +} + +static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + struct bluetooth_data *data = io->private_data; + struct ipc_data_cfg cfg = data->cfg; + + snd_pcm_sframes_t ret = 0; + + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", + areas->step, areas->first, offset, size, io->nonblock); + + if (data->count == 0) { + int nrecv; + + nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, + MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0 )); + + if (nrecv == cfg.pkt_len) { + ret = 0; + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + } + else if (nrecv > 0) { + ret = -EIO; + SNDERR(strerror(-ret)); + } + else if (nrecv == -1 && errno == EAGAIN) { + ret = -EAGAIN; + } + else { /* nrecv < 0 */ + /* EPIPE means device underrun in ALSA world. But we mean we lost contact + with server, so we have to find another error code */ + ret = (errno == EPIPE ? -EIO : -errno); + SYSERR("Lost contact with headsetd"); + } + } + if(ret == 0) { /* Still ok, proceed */ + snd_pcm_uframes_t frames_to_write; + unsigned char *buff; + + buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + + if((data->count + cfg.sample_size * size) <= cfg.pkt_len) + frames_to_write = size; + else + frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size; + + memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write); + data->count += (areas->step / 8 * frames_to_write); + data->count %= cfg.pkt_len; + /* Return written frames count */ + ret = frames_to_write; + } + + DBG("returning %d", (int)ret); + return ret; +} + +static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + struct bluetooth_data *data = io->private_data; + struct ipc_data_cfg cfg = data->cfg; + snd_pcm_sframes_t ret = 0; + snd_pcm_uframes_t frames_to_read; + unsigned char *buff; + + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", + areas->step, areas->first, offset, size, io->nonblock); + + if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) + frames_to_read = size; + else + frames_to_read = (cfg.pkt_len - data->count) / cfg.sample_size; + + /* Ready for more data */ + buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read); + + if ((data->count + areas->step / 8 * frames_to_read) == cfg.pkt_len) { + int rsend; + /* Actually send packet */ + rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); + if (rsend > 0) { + /* Reset count pointer */ + data->count = 0; + + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + + ret = frames_to_read; + } + else { + /* EPIPE means device underrun in ALSA world. But we mean we lost contact + with server, so we have to find another error code */ + ret = (errno == EPIPE ? -EIO : -errno); + if(errno == EPIPE) + SYSERR("Lost contact with headsetd"); + } + } + else { + /* Remember we have some frame in the pipe now */ + data->count += areas->step / 8 * frames_to_read; + /* Ask for more */ + ret = frames_to_read; + } + + DBG("returning %d", (int)ret); + return ret; +} + static snd_pcm_ioplug_callback_t bluetooth_playback_callback = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, -#if 0 .hw_params = bluetooth_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_write, -#endif }; static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { @@ -94,11 +263,9 @@ static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, -#if 0 .hw_params = bluetooth_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_read, -#endif }; #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -146,42 +313,66 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return 0; } -SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) +static int bluetooth_cfg(struct bluetooth_data *data) { - snd_config_iterator_t iter, next; - struct bluetooth_data *data; - struct sockaddr_un addr; - unsigned int id; - int sk, err; - - DBG("Bluetooth PCM plugin (%s)", - stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - - snd_config_for_each(iter, next, conf) { - snd_config_t *n = snd_config_iterator_entry(iter); - const char *id; + struct ipc_packet pkt; + int res; + + DBG("Sending PKT_TYPE_CFG_REQ..."); + pkt.type = PKT_TYPE_CFG_REQ; + pkt.role = PKT_ROLE_NONE; + res = send(data->sock, &pkt, sizeof(struct ipc_packet), 0); + if (res < 0) + return errno; + DBG("OK - %d bytes sent", res); + + DBG("Waiting for response..."); + do { + int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + struct ipc_packet *pkt_ptr; + + pkt_ptr = malloc(sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg)); + res = recv(data->sock, pkt_ptr, len, MSG_WAITALL | (data->io.nonblock ? MSG_DONTWAIT : 0 )); + } while ((res < 0) && (errno == EINTR)); + if (res < 0) + return -errno; + DBG("OK - %d bytes received", res); - if (snd_config_get_id(n, &id) < 0) - continue; + if (pkt.type != PKT_TYPE_CFG_RSP) { + SNDERR("Unexpected packet type received: type = %d", pkt.type); + return -EINVAL; + } - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) - continue; + if (pkt.error != PKT_ERROR_NONE) { + SNDERR("Error while configuring device: error = %d", pkt.error); + return pkt.error; + } - if (strcmp(id, "bdaddr") == 0) { - const char *str; - if (snd_config_get_string(n, &str) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - printf("bdaddr %s\n", str); - continue; - } + if (pkt.length != sizeof(struct ipc_data_cfg)) { + SNDERR("Error while configuring device: packet size doesn't match"); + return -EINVAL; + } - SNDERR("Unknown field %s", id); + memcpy(&data->cfg, &pkt.data, pkt.length); + if (data->cfg.fd == -1) { + SNDERR("Error while configuring device: could not acquire audio socket"); return -EINVAL; } + DBG("Device configuration:"); + DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u, rate=%u", + data->cfg.fd, data->cfg.fd_opt, data->cfg.channels, + data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); + + return 0; +} + +static int bluetooth_init(struct bluetooth_data *data) +{ + int sk, err, id; + struct sockaddr_un addr; + id = abs(getpid() * rand()); sk = socket(PF_LOCAL, SOCK_DGRAM, 0); @@ -195,6 +386,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", IPC_SOCKET_NAME, id); + DBG("Binding address: %s", addr.sun_path + 1); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't bind socket"); close(sk); @@ -205,6 +397,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) addr.sun_family = AF_UNIX; snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); + DBG("Connecting to address: %s", addr.sun_path + 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { SNDERR("Can't connect socket"); close(sk); @@ -221,34 +414,62 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->sock = sk; - data->io.version = SND_PCM_IOPLUG_VERSION; - data->io.name = "Bluetooth Audio"; - data->io.mmap_rw = 0; /* No direct mmap communication */ + if ((err = bluetooth_cfg(data)) < 0) + return err; + + data->buffer = malloc(data->cfg.pkt_len); + + memset(data->buffer, 0, data->cfg.pkt_len); - data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? + return 0; +} + +SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) +{ +// snd_config_iterator_t iter, next; + struct bluetooth_data data; + int err; + + DBG("Bluetooth PCM plugin blablabla (%s)", + stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); + +// snd_config_for_each(iter, next, conf) { +// } + + DBG("Initing Bluetooth..."); + err = bluetooth_init(&data); + if (err < 0) + goto error; + DBG("Done"); + + data.io.version = SND_PCM_IOPLUG_VERSION; + data.io.name = "Bluetooth Audio Device"; + data.io.mmap_rw = 0; /* No direct mmap communication */ + + data.io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &bluetooth_playback_callback : &bluetooth_capture_callback; - data->io.poll_fd = sk; - data->io.poll_events = POLLIN; - data->io.private_data = data; + data.io.poll_fd = data.cfg.fd; + data.io.poll_events = POLLIN; + data.io.private_data = &data; - err = snd_pcm_ioplug_create(&data->io, name, stream, mode); + err = snd_pcm_ioplug_create(&data.io, name, stream, mode); if (err < 0) goto error; - err = bluetooth_hw_constraint(&data->io); + err = bluetooth_hw_constraint(&data.io); if (err < 0) { - snd_pcm_ioplug_delete(&data->io); - goto error; + snd_pcm_ioplug_delete(&data.io); + goto error; } - *pcmp = data->io.pcm; + *pcmp = data.io.pcm; return 0; error: - close(sk); + close(data.sock); - free(data); + free(&data); return err; } diff --git a/audio/unix.c b/audio/unix.c index ce2041b2..8cd1affc 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -48,7 +48,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; socklen_t addrlen; - unsigned char buf[128]; + struct ipc_packet pkt; + struct ipc_data_cfg cfg; int sk, len; debug("chan %p cond %td data %p", chan, cond, data); @@ -66,10 +67,37 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); - len = recvfrom(sk, buf, sizeof(buf), 0, (struct sockaddr *) &addr, &addrlen); + len = recvfrom(sk, &pkt, sizeof(pkt), 0, (struct sockaddr *) &addr, &addrlen); debug("path %s len %d", addr.sun_path + 1, len); + switch (pkt.type) { + case PKT_TYPE_CFG_REQ: + info("Package PKT_TYPE_CFG_REQ:%u", pkt.role); + struct ipc_data_cfg *cfg_ptr; + + cfg.fd = -1; + cfg.fd_opt = CFG_FD_OPT_READWRITE; + cfg.encoding = 0; + cfg.bitpool = 0; + cfg.channels = 1; + cfg.pkt_len = 48; + cfg.sample_size = 2; + cfg.rate = 8000; + + cfg_ptr = (struct ipc_data_cfg *) &pkt.data; + pkt.type = PKT_TYPE_CFG_RSP; + cfg_ptr[0] = cfg; + pkt.length = sizeof(struct ipc_data_cfg); + len = send(sk, &pkt, sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg), 0); + break; + case PKT_TYPE_STATUS_REQ: + info("Package PKT_TYPE_STATUS_REQ"); + break; + case PKT_TYPE_CTL_REQ: + info("Package PKT_TYPE_CTL_REQ"); + break; + } return TRUE; } @@ -106,6 +134,8 @@ int unix_init(void) g_io_channel_unref(io); + info("Unix socket created: %d", sk); + return 0; } -- cgit From d61880a2d81b1d7a56d2377a8a5063106a10d2f4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 08:36:35 +0000 Subject: Cleanup --- audio/headset.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 45f989bc..78579b5e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -378,7 +378,7 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *devi static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; int ret, sk, err, flags; socklen_t len; DBusMessage *reply; @@ -386,8 +386,12 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; - assert(hs != NULL && hs->pending_connect != NULL && - hs->sco == NULL && hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); + hs = device->headset; + + assert(hs != NULL); + assert(hs->pending_connect != NULL); + assert(hs->sco == NULL); + assert(hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); sk = g_io_channel_unix_get_fd(chan); @@ -1336,8 +1340,6 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, if (!c) return DBUS_HANDLER_RESULT_NEED_MEMORY; - hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - c->msg = msg ? dbus_message_ref(msg) : NULL; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); @@ -1383,12 +1385,15 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, goto failed; } - debug("Connect in progress"); + debug("SCO connect in progress"); + + hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, (GIOFunc) sco_connect_cb, device); } else { - debug("Connect succeeded with first try"); + debug("SCO connect succeeded with first try"); + hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; sco_connect_cb(c->io, G_IO_OUT, device); } -- cgit From 77c1d7b9ca0e99693fd084f5541fb175f3002e6b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 09:12:42 +0000 Subject: Properly handle a SCO connect() succeeding on the first try --- audio/headset.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 78579b5e..a3921d36 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1325,6 +1325,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, struct headset *hs = device->headset; struct sockaddr_sco addr; struct pending_connect *c; + gboolean do_callback = FALSE; int sk, err; if (hs->state < HEADSET_STATE_CONNECTED) @@ -1386,19 +1387,19 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, } debug("SCO connect in progress"); - - hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, (GIOFunc) sco_connect_cb, device); } else { debug("SCO connect succeeded with first try"); - hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - sco_connect_cb(c->io, G_IO_OUT, device); + do_callback = TRUE; } + hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; hs->pending_connect = c; + if (do_callback) + sco_connect_cb(c->io, G_IO_OUT, device); + return 0; failed: -- cgit From fa8443140be92fdf58acdf98a31bb3a986576cbe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 09:19:25 +0000 Subject: Some cleanup of asserts and error handling --- audio/headset.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a3921d36..b58aa903 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -521,8 +521,9 @@ static int rfcomm_connect(audio_device_t *device, int *err) char address[18]; int sk; - assert(hs != NULL && hs->pending_connect != NULL && - hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); + assert(hs != NULL); + assert(hs->pending_connect != NULL); + assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; @@ -551,7 +552,8 @@ static int rfcomm_connect(audio_device_t *device, int *err) } if (set_nonblocking(sk) < 0) { - *err = errno; + if (err) + *err = errno; goto failed; } @@ -562,6 +564,8 @@ static int rfcomm_connect(audio_device_t *device, int *err) hs->pending_connect->io = g_io_channel_unix_new(sk); if (!hs->pending_connect->io) { + if (err) + *err = ENOMEM; error("channel_unix_new failed in rfcomm connect"); goto failed; } @@ -812,7 +816,10 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct headset *hs = device->headset; struct pending_connect *c; - assert(hs != NULL && hs->pending_connect && !hs->rfcomm); + assert(hs != NULL); + assert(hs->pending_connect); + assert(!hs->rfcomm); + c = hs->pending_connect; reply = dbus_pending_call_steal_reply(call); @@ -1057,7 +1064,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data) dbus_uint32_t handle; int array_len; - assert(hs != NULL && hs->pending_connect); + assert(hs != NULL); + assert(hs->pending_connect); + c = hs->pending_connect; reply = dbus_pending_call_steal_reply(call); -- cgit From 60e90bcc4d46b26ec056821f3024b08d364f1cc7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 09:24:37 +0000 Subject: Another minor fix --- audio/headset.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b58aa903..5fc33c43 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -447,7 +447,7 @@ failed: static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { - struct headset *hs = device->headset; + struct headset *hs; char hs_address[18]; int sk, ret, err; socklen_t len; @@ -455,6 +455,8 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; + hs = device->headset; + assert(hs != NULL); assert(hs->pending_connect != NULL); assert(hs->rfcomm == NULL); -- cgit From d951ee9dae5061b8db7ef875e2f477d4445557a4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 09:35:31 +0000 Subject: Add more HFP support --- audio/headset.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 5fc33c43..0b6d4e51 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -301,7 +301,12 @@ static void send_cancel_auth(audio_device_t *device) { DBusMessage *cancel; char addr[18], *address = addr; - const char *uuid = HSP_AG_UUID; + const char *uuid; + + if (device->headset->type == SVC_HEADSET) + uuid = HSP_AG_UUID; + else + uuid = HFP_AG_UUID; cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", @@ -527,8 +532,6 @@ static int rfcomm_connect(audio_device_t *device, int *err) assert(hs->pending_connect != NULL); assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; - ba2str(&device->bda, address); debug("Connecting to %s channel %d", address, hs->rfcomm_ch); @@ -1158,7 +1161,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, DBusPendingCall *pending; audio_device_t *device = data; struct headset *hs = device->headset; - const char *hs_svc = "hsp"; + const char *hs_svc; const char *addr_ptr; char hs_address[18]; int err; @@ -1178,6 +1181,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL; + hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + if (hs->rfcomm_ch > 0) { if (rfcomm_connect(device, &err) < 0) { error("Unable to connect"); @@ -1200,6 +1205,11 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } + if (hs->type == SVC_HEADSET) + hs_svc = "hsp"; + else + hs_svc = "hfp"; + ba2str(&device->bda, hs_address); addr_ptr = hs_address; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, @@ -1576,7 +1586,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * struct sockaddr_rc addr; socklen_t size; char hs_address[18], *address = hs_address; - const char *uuid = HSP_AG_UUID; + const char *uuid; audio_device_t *device; struct headset *hs; DBusMessage *auth; @@ -1622,10 +1632,13 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * return TRUE; } - if (chan == hs_server) + if (chan == hs_server) { hs->type = SVC_HEADSET; - else + uuid = HSP_AG_UUID; + } else { hs->type = SVC_HANDSFREE; + uuid = HFP_AG_UUID; + } auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", "RequestAuthorization"); -- cgit From e47ffdb86e691d7418c0e5a2bebbdd7c512aa50f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 6 Jun 2007 10:11:06 +0000 Subject: Fix passing the right parameter to hs_disconnect --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0b6d4e51..2fb139ad 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1152,7 +1152,7 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - hs_disconnect(NULL, NULL, hs); + hs_disconnect(NULL, NULL, device); } static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, -- cgit From f1a8e719f5687bb32f2f5ed59f26a3203ad43efb Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 6 Jun 2007 17:58:19 +0000 Subject: Some fixes for audio code prototype. --- audio/headset.c | 17 +++++++++ audio/headset.h | 4 +++ audio/manager.c | 11 ++++++ audio/manager.h | 1 + audio/pcm_bluetooth.c | 98 ++++++++++++++++++++++++++------------------------- audio/unix.c | 45 +++++++++++------------ 6 files changed, 106 insertions(+), 70 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 2fb139ad..109e7c5d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1817,3 +1817,20 @@ void headset_exit(void) dbus_connection_unref(connection); connection = NULL; } + +int headset_get_config(headset_t *headset, struct ipc_data_cfg *cfg) +{ + if (headset->sco == NULL) + return -1; + + cfg->fd = g_io_channel_unix_get_fd(headset->sco); + cfg->fd_opt = CFG_FD_OPT_READWRITE; + cfg->encoding = 0; + cfg->bitpool = 0; + cfg->channels = 1; + cfg->pkt_len = 48; + cfg->sample_size = 2; + cfg->rate = 8000; + + return 0; +} diff --git a/audio/headset.h b/audio/headset.h index 0325e755..6780e7a9 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -29,6 +29,8 @@ #include +#include "ipc.h" + #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" typedef struct headset headset_t; @@ -47,4 +49,6 @@ int headset_server_init(DBusConnection *conn, gboolean disable_hfp, void headset_exit(void); +int headset_get_config(headset_t *headset, struct ipc_data_cfg *cfg); + #endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/manager.c b/audio/manager.c index 1b727f7a..df0f550f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1286,3 +1286,14 @@ void audio_exit(void) connection = NULL; } + +int manager_get_device(uint8_t role, struct ipc_data_cfg *cfg) +{ + if (default_hs == NULL || default_hs->headset == NULL) + return -1; + + if (!headset_is_connected(default_hs->headset)) + return -1; + + return headset_get_config(default_hs->headset, cfg); +} diff --git a/audio/manager.h b/audio/manager.h index 45d93d7f..1927a110 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -94,3 +94,4 @@ DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc); +int manager_get_device(uint8_t role, struct ipc_data_cfg *cfg); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 29583940..c8988edc 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -115,7 +115,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params struct ipc_data_cfg cfg = data->cfg; uint32_t period_count = io->buffer_size / io->period_size; - DBG("period_count = %d", period_count); + DBG("fd = %d, period_count = %d", cfg.fd, period_count); if(setsockopt(cfg.fd, SOL_SCO, io->stream == SND_PCM_STREAM_PLAYBACK ? SCO_TXBUFS : SCO_RXBUFS, @@ -315,56 +315,59 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_cfg(struct bluetooth_data *data) { - struct ipc_packet pkt; int res; + int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + struct ipc_packet *pkt; DBG("Sending PKT_TYPE_CFG_REQ..."); - pkt.type = PKT_TYPE_CFG_REQ; - pkt.role = PKT_ROLE_NONE; - res = send(data->sock, &pkt, sizeof(struct ipc_packet), 0); + pkt = malloc(len); + memset(pkt, 0, len); + pkt->type = PKT_TYPE_CFG_REQ; + pkt->role = PKT_ROLE_NONE; + pkt->error = PKT_ERROR_NONE; + res = send(data->sock, pkt, len, 0); if (res < 0) return errno; DBG("OK - %d bytes sent", res); DBG("Waiting for response..."); - do { - int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); - struct ipc_packet *pkt_ptr; - pkt_ptr = malloc(sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg)); - res = recv(data->sock, pkt_ptr, len, MSG_WAITALL | (data->io.nonblock ? MSG_DONTWAIT : 0 )); - } while ((res < 0) && (errno == EINTR)); + memset(pkt, 0, len); + res = recv(data->sock, pkt, len, 0); + if (res < 0) return -errno; DBG("OK - %d bytes received", res); - if (pkt.type != PKT_TYPE_CFG_RSP) { - SNDERR("Unexpected packet type received: type = %d", pkt.type); + if (pkt->type != PKT_TYPE_CFG_RSP) { + SNDERR("Unexpected packet type received: type = %d", pkt->type); return -EINVAL; } - if (pkt.error != PKT_ERROR_NONE) { - SNDERR("Error while configuring device: error = %d", pkt.error); - return pkt.error; + if (pkt->error != PKT_ERROR_NONE) { + SNDERR("Error while configuring device: error = %d", pkt->error); + return pkt->error; } - if (pkt.length != sizeof(struct ipc_data_cfg)) { + if (pkt->length != sizeof(struct ipc_data_cfg)) { SNDERR("Error while configuring device: packet size doesn't match"); return -EINVAL; } - memcpy(&data->cfg, &pkt.data, pkt.length); - - if (data->cfg.fd == -1) { - SNDERR("Error while configuring device: could not acquire audio socket"); - return -EINVAL; - } + memcpy(&data->cfg, pkt->data, sizeof(struct ipc_data_cfg)); DBG("Device configuration:"); + DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u, rate=%u", data->cfg.fd, data->cfg.fd_opt, data->cfg.channels, data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); + if (data->cfg.fd == -1) { + SNDERR("Error while configuring device: could not acquire audio socket"); + return -EINVAL; + } + + free(pkt); return 0; } @@ -373,6 +376,9 @@ static int bluetooth_init(struct bluetooth_data *data) int sk, err, id; struct sockaddr_un addr; + if (!data) + return -EINVAL; + id = abs(getpid() * rand()); sk = socket(PF_LOCAL, SOCK_DGRAM, 0); @@ -404,18 +410,12 @@ static int bluetooth_init(struct bluetooth_data *data) return -errno; } - data = malloc(sizeof(*data)); - if (!data) { - close(sk); - return -ENOMEM; - } - - memset(data, 0, sizeof(*data)); - data->sock = sk; - if ((err = bluetooth_cfg(data)) < 0) + if ((err = bluetooth_cfg(data)) < 0) { + free(data); return err; + } data->buffer = malloc(data->cfg.pkt_len); @@ -427,7 +427,7 @@ static int bluetooth_init(struct bluetooth_data *data) SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) { // snd_config_iterator_t iter, next; - struct bluetooth_data data; + struct bluetooth_data *data; int err; DBG("Bluetooth PCM plugin blablabla (%s)", @@ -437,39 +437,41 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) // } DBG("Initing Bluetooth..."); - err = bluetooth_init(&data); + data = malloc(sizeof(struct bluetooth_data)); + memset(data, 0, sizeof(struct bluetooth_data)); + err = bluetooth_init(data); if (err < 0) goto error; DBG("Done"); - data.io.version = SND_PCM_IOPLUG_VERSION; - data.io.name = "Bluetooth Audio Device"; - data.io.mmap_rw = 0; /* No direct mmap communication */ + data->io.version = SND_PCM_IOPLUG_VERSION; + data->io.name = "Bluetooth Audio Device"; + data->io.mmap_rw = 0; /* No direct mmap communication */ - data.io.callback = stream == SND_PCM_STREAM_PLAYBACK ? + data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &bluetooth_playback_callback : &bluetooth_capture_callback; - data.io.poll_fd = data.cfg.fd; - data.io.poll_events = POLLIN; - data.io.private_data = &data; + data->io.poll_fd = data->cfg.fd; + data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? + POLLOUT : POLLIN; + data->io.private_data = data; - err = snd_pcm_ioplug_create(&data.io, name, stream, mode); + err = snd_pcm_ioplug_create(&data->io, name, stream, mode); if (err < 0) goto error; - err = bluetooth_hw_constraint(&data.io); + err = bluetooth_hw_constraint(&data->io); if (err < 0) { - snd_pcm_ioplug_delete(&data.io); + snd_pcm_ioplug_delete(&data->io); goto error; } - *pcmp = data.io.pcm; + *pcmp = data->io.pcm; return 0; error: - close(data.sock); - - free(&data); + close(data->sock); + free(data); return err; } diff --git a/audio/unix.c b/audio/unix.c index 8cd1affc..f2bf2271 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -39,8 +39,8 @@ #include "logging.h" #include "dbus.h" -#include "ipc.h" #include "unix.h" +#include "manager.h" static int unix_sock = -1; @@ -48,8 +48,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; socklen_t addrlen; - struct ipc_packet pkt; - struct ipc_data_cfg cfg; + struct ipc_packet *pkt; + struct ipc_data_cfg *cfg; int sk, len; debug("chan %p cond %td data %p", chan, cond, data); @@ -67,29 +67,28 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); - len = recvfrom(sk, &pkt, sizeof(pkt), 0, (struct sockaddr *) &addr, &addrlen); + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + pkt = g_malloc0(len); + len = recvfrom(sk, pkt, len, 0, (struct sockaddr *) &addr, &addrlen); debug("path %s len %d", addr.sun_path + 1, len); - switch (pkt.type) { + switch (pkt->type) { case PKT_TYPE_CFG_REQ: - info("Package PKT_TYPE_CFG_REQ:%u", pkt.role); - struct ipc_data_cfg *cfg_ptr; - - cfg.fd = -1; - cfg.fd_opt = CFG_FD_OPT_READWRITE; - cfg.encoding = 0; - cfg.bitpool = 0; - cfg.channels = 1; - cfg.pkt_len = 48; - cfg.sample_size = 2; - cfg.rate = 8000; - - cfg_ptr = (struct ipc_data_cfg *) &pkt.data; - pkt.type = PKT_TYPE_CFG_RSP; - cfg_ptr[0] = cfg; - pkt.length = sizeof(struct ipc_data_cfg); - len = send(sk, &pkt, sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg), 0); + info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); + + cfg = (struct ipc_data_cfg *) pkt->data; + + if (manager_get_device(pkt->role, cfg) < 0) + cfg->fd = -1; + + pkt->type = PKT_TYPE_CFG_RSP; + pkt->length = sizeof(struct ipc_data_cfg); + pkt->error = PKT_ERROR_NONE; + len = sendto(sk, pkt, len, 0, (struct sockaddr *) &addr, addrlen); + if (len < 0) + info("Error %s(%d)", strerror(errno), errno); + info("%d bytes sent", len); break; case PKT_TYPE_STATUS_REQ: info("Package PKT_TYPE_STATUS_REQ"); @@ -98,6 +97,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) info("Package PKT_TYPE_CTL_REQ"); break; } + + g_free(pkt); return TRUE; } -- cgit From 8acc6d7b78f162cff2d9be534ff9acd23076d0e1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 7 Jun 2007 08:09:57 +0000 Subject: Fix CreateHeadset to behave properly if an existing object was found --- audio/manager.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index df0f550f..8c9fb7d9 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -940,6 +940,7 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms DBusMessage *reply; DBusError derr; audio_device_t *device; + gboolean created = FALSE; dbus_error_init(&derr); dbus_message_get_args(msg, &derr, @@ -954,15 +955,18 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms str2ba(address, &bda); device = find_device(&bda); - if (!device) { - device = create_device(&bda); - if (!add_device(device)) { - free_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to create new audio device"); - } - } + if (device) + goto done; + + device = create_device(&bda); + if (!add_device(device)) { + free_device(device); + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to create new audio device"); + } + + created = TRUE; if (!device->headset) { device->headset = headset_init(device->object_path, NULL, 0); @@ -974,17 +978,19 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms } } +done: path = device->object_path; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "HeadsetCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (created) + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); -- cgit From 16441b2106ab7e7712ed21dd7a68ba308afc9942 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 7 Jun 2007 08:18:58 +0000 Subject: Fix whitespace --- audio/manager.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 8c9fb7d9..18a653da 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -955,18 +955,18 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms str2ba(address, &bda); device = find_device(&bda); - if (device) - goto done; - - device = create_device(&bda); - if (!add_device(device)) { - free_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to create new audio device"); - } - - created = TRUE; + if (device) + goto done; + + device = create_device(&bda); + if (!add_device(device)) { + free_device(device); + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to create new audio device"); + } + + created = TRUE; if (!device->headset) { device->headset = headset_init(device->object_path, NULL, 0); -- cgit From 00c71248e732de0230c12df4240087a6b2747179 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 7 Jun 2007 12:26:17 +0000 Subject: Major cleanup after importing code from plugz --- audio/pcm_bluetooth.c | 237 +++++++++++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 109 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index c8988edc..6ca86cf4 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -96,14 +96,17 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); + DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", + io->period_size, io->buffer_size); if (io->stream == SND_PCM_STREAM_PLAYBACK) { - /* If not null for playback, xmms doesn't display time correctly */ + /* If not null for playback, xmms doesn't display time + * correctly */ data->hw_ptr = 0; } else { - /* ALSA library is really picky on the fact hw_ptr is not null. If it is, capture won't start */ + /* ALSA library is really picky on the fact hw_ptr is not null. + * If it is, capture won't start */ data->hw_ptr = io->period_size; } return 0; @@ -114,23 +117,28 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; uint32_t period_count = io->buffer_size / io->period_size; + int opt_name; + + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SCO_TXBUFS : SCO_RXBUFS; DBG("fd = %d, period_count = %d", cfg.fd, period_count); - if(setsockopt(cfg.fd, SOL_SCO, - io->stream == SND_PCM_STREAM_PLAYBACK ? SCO_TXBUFS : SCO_RXBUFS, - &period_count, - sizeof(period_count)) == 0) { + if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, + sizeof(period_count)) == 0) return 0; - } else if(setsockopt(cfg.fd, SOL_SCO, - io->stream == SND_PCM_STREAM_PLAYBACK ? SO_SNDBUF : SO_RCVBUF, - &period_count, - sizeof(period_count)) == 0) { + + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SO_SNDBUF : SO_RCVBUF; + + if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, + sizeof(period_count)) == 0) return 0; - } else { - SNDERR("Unable to set number of SCO buffers : please upgrade your Kernel !"); - return -EINVAL; - } + + SNDERR("Unable to set number of SCO buffers: please upgrade your" + "kernel!"); + + return -EINVAL; } static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, @@ -140,56 +148,50 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; - - snd_pcm_sframes_t ret = 0; + snd_pcm_uframes_t frames_to_write, ret; + unsigned char *buff; + int nrecv; DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); - if (data->count == 0) { - int nrecv; - - nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, - MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0 )); - - if (nrecv == cfg.pkt_len) { - ret = 0; - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; - } - else if (nrecv > 0) { - ret = -EIO; - SNDERR(strerror(-ret)); - } - else if (nrecv == -1 && errno == EAGAIN) { - ret = -EAGAIN; - } - else { /* nrecv < 0 */ - /* EPIPE means device underrun in ALSA world. But we mean we lost contact - with server, so we have to find another error code */ - ret = (errno == EPIPE ? -EIO : -errno); - SYSERR("Lost contact with headsetd"); - } + if (data->count > 0) + goto proceed; + + nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, + MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); + + if (nrecv < 0) { + ret = (errno == EPIPE) ? -EIO : -errno; + goto done; } - if(ret == 0) { /* Still ok, proceed */ - snd_pcm_uframes_t frames_to_write; - unsigned char *buff; - - buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; - - if((data->count + cfg.sample_size * size) <= cfg.pkt_len) - frames_to_write = size; - else - frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size; - - memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write); - data->count += (areas->step / 8 * frames_to_write); - data->count %= cfg.pkt_len; - /* Return written frames count */ - ret = frames_to_write; + + if (nrecv != cfg.pkt_len) { + ret = -EIO; + SNDERR(strerror(-ret)); + goto done; } - DBG("returning %d", (int)ret); + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + +proceed: + buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + + if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) + frames_to_write = size; + else + frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size; + + memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write); + data->count += (areas->step / 8 * frames_to_write); + data->count %= cfg.pkt_len; + + /* Return written frames count */ + ret = frames_to_write; + +done: + DBG("returning %lu", ret); return ret; } @@ -203,9 +205,11 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; unsigned char *buff; + int rsend; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", - areas->step, areas->first, offset, size, io->nonblock); + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," + "io->nonblock=%u", areas->step, areas->first, + offset, size, io->nonblock); if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) frames_to_read = size; @@ -216,34 +220,28 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read); - if ((data->count + areas->step / 8 * frames_to_read) == cfg.pkt_len) { - int rsend; - /* Actually send packet */ - rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); - if (rsend > 0) { - /* Reset count pointer */ - data->count = 0; - - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; - - ret = frames_to_read; - } - else { - /* EPIPE means device underrun in ALSA world. But we mean we lost contact - with server, so we have to find another error code */ - ret = (errno == EPIPE ? -EIO : -errno); - if(errno == EPIPE) - SYSERR("Lost contact with headsetd"); - } - } - else { + if ((data->count + areas->step / 8 * frames_to_read) != cfg.pkt_len) { /* Remember we have some frame in the pipe now */ data->count += areas->step / 8 * frames_to_read; - /* Ask for more */ ret = frames_to_read; + goto done; } + rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); + if (rsend > 0) { + /* Reset count pointer */ + data->count = 0; + + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + + ret = frames_to_read; + } else if (rsend < 0) + ret = (errno == EPIPE) ? -EIO : -errno; + else + ret = -EIO; + +done: DBG("returning %d", (int)ret); return ret; } @@ -320,37 +318,46 @@ static int bluetooth_cfg(struct bluetooth_data *data) struct ipc_packet *pkt; DBG("Sending PKT_TYPE_CFG_REQ..."); + pkt = malloc(len); + if (!pkt) + return -ENOMEM; + memset(pkt, 0, len); pkt->type = PKT_TYPE_CFG_REQ; pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; + res = send(data->sock, pkt, len, 0); if (res < 0) - return errno; + return -errno; + DBG("OK - %d bytes sent", res); DBG("Waiting for response..."); memset(pkt, 0, len); res = recv(data->sock, pkt, len, 0); - if (res < 0) return -errno; + DBG("OK - %d bytes received", res); if (pkt->type != PKT_TYPE_CFG_RSP) { - SNDERR("Unexpected packet type received: type = %d", pkt->type); + SNDERR("Unexpected packet type received: type = %d", + pkt->type); return -EINVAL; } if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error while configuring device: error = %d", pkt->error); + SNDERR("Error while configuring device: error = %d", + pkt->error); return pkt->error; } if (pkt->length != sizeof(struct ipc_data_cfg)) { - SNDERR("Error while configuring device: packet size doesn't match"); + SNDERR("Error while configuring device: packet size doesn't" + "match"); return -EINVAL; } @@ -358,16 +365,19 @@ static int bluetooth_cfg(struct bluetooth_data *data) DBG("Device configuration:"); - DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u, rate=%u", - data->cfg.fd, data->cfg.fd_opt, data->cfg.channels, - data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); + DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," + "rate=%u", data->cfg.fd, data->cfg.fd_opt, + data->cfg.channels, data->cfg.pkt_len, + data->cfg.sample_size, data->cfg.rate); if (data->cfg.fd == -1) { - SNDERR("Error while configuring device: could not acquire audio socket"); + SNDERR("Error while configuring device: could not acquire" + "audio socket"); return -EINVAL; } free(pkt); + return 0; } @@ -379,10 +389,15 @@ static int bluetooth_init(struct bluetooth_data *data) if (!data) return -EINVAL; + memset(data, 0, sizeof(struct bluetooth_data)); + + data->sock = -1; + id = abs(getpid() * rand()); sk = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sk < 0) { + err = -errno; SNDERR("Can't open socket"); return -errno; } @@ -394,9 +409,10 @@ static int bluetooth_init(struct bluetooth_data *data) DBG("Binding address: %s", addr.sun_path + 1); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; SNDERR("Can't bind socket"); close(sk); - return -errno; + return err; } memset(&addr, 0, sizeof(addr)); @@ -405,19 +421,21 @@ static int bluetooth_init(struct bluetooth_data *data) DBG("Connecting to address: %s", addr.sun_path + 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; SNDERR("Can't connect socket"); close(sk); - return -errno; + return err; } data->sock = sk; - if ((err = bluetooth_cfg(data)) < 0) { - free(data); + err = bluetooth_cfg(data); + if (err < 0) return err; - } data->buffer = malloc(data->cfg.pkt_len); + if (!data->buffer) + return -ENOMEM; memset(data->buffer, 0, data->cfg.pkt_len); @@ -426,33 +444,31 @@ static int bluetooth_init(struct bluetooth_data *data) SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) { -// snd_config_iterator_t iter, next; struct bluetooth_data *data; int err; - DBG("Bluetooth PCM plugin blablabla (%s)", + DBG("Bluetooth PCM plugin (%s)", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); -// snd_config_for_each(iter, next, conf) { -// } - - DBG("Initing Bluetooth..."); data = malloc(sizeof(struct bluetooth_data)); - memset(data, 0, sizeof(struct bluetooth_data)); + if (!data) { + err = -ENOMEM; + goto error; + } + err = bluetooth_init(data); if (err < 0) goto error; - DBG("Done"); data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; - data->io.mmap_rw = 0; /* No direct mmap communication */ + data->io.mmap_rw = 0; /* No direct mmap communication */ data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &bluetooth_playback_callback : &bluetooth_capture_callback; data->io.poll_fd = data->cfg.fd; data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? - POLLOUT : POLLIN; + POLLOUT : POLLIN; data->io.private_data = data; err = snd_pcm_ioplug_create(&data->io, name, stream, mode); @@ -470,8 +486,11 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) return 0; error: - close(data->sock); - free(data); + if (data) { + if (data->sock >= 0) + close(data->sock); + free(data); + } return err; } -- cgit From d95acb81523269d5e00485bc1350f8b3e6ee0050 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 7 Jun 2007 12:33:56 +0000 Subject: More cleanup --- audio/pcm_bluetooth.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6ca86cf4..5057addb 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -313,8 +313,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_cfg(struct bluetooth_data *data) { - int res; - int len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); struct ipc_packet *pkt; DBG("Sending PKT_TYPE_CFG_REQ..."); @@ -328,37 +327,50 @@ static int bluetooth_cfg(struct bluetooth_data *data) pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; - res = send(data->sock, pkt, len, 0); - if (res < 0) - return -errno; + ret = send(data->sock, pkt, len, 0); + if (ret < 0) { + ret = -errno; + goto done; + } else if (ret == 0) { + ret = -EIO; + goto done; + } - DBG("OK - %d bytes sent", res); + DBG("OK - %d bytes sent", ret); DBG("Waiting for response..."); memset(pkt, 0, len); - res = recv(data->sock, pkt, len, 0); - if (res < 0) - return -errno; + ret = recv(data->sock, pkt, len, 0); + if (ret < 0) { + ret = -errno; + goto done; + } else if (ret == 0) { + ret = -EIO; + goto done; + } - DBG("OK - %d bytes received", res); + DBG("OK - %d bytes received", ret); if (pkt->type != PKT_TYPE_CFG_RSP) { SNDERR("Unexpected packet type received: type = %d", pkt->type); - return -EINVAL; + ret = -EINVAL; + goto done; } if (pkt->error != PKT_ERROR_NONE) { SNDERR("Error while configuring device: error = %d", pkt->error); - return pkt->error; + ret = pkt->error; + goto done; } if (pkt->length != sizeof(struct ipc_data_cfg)) { SNDERR("Error while configuring device: packet size doesn't" "match"); - return -EINVAL; + ret = -EINVAL; + goto done; } memcpy(&data->cfg, pkt->data, sizeof(struct ipc_data_cfg)); @@ -373,12 +385,15 @@ static int bluetooth_cfg(struct bluetooth_data *data) if (data->cfg.fd == -1) { SNDERR("Error while configuring device: could not acquire" "audio socket"); - return -EINVAL; + ret = -EINVAL; + goto done; } - free(pkt); + ret = 0; - return 0; +done: + free(pkt); + return ret; } static int bluetooth_init(struct bluetooth_data *data) -- cgit From 019dd58ff876fcdaae5a7cd257822955d28b5b96 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Fri, 8 Jun 2007 17:23:29 +0000 Subject: backward compatible to old sco patch --- audio/pcm_bluetooth.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 5057addb..f2d3c275 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -135,7 +135,16 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params sizeof(period_count)) == 0) return 0; - SNDERR("Unable to set number of SCO buffers: please upgrade your" + /* backward compatible to the old patch */ + + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SCO_TXBUFS : SCO_RXBUFS; + + if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, + sizeof(period_count)) == 0) + return 0; + + SNDERR("Unable to set number of SCO buffers: please upgrade your " "kernel!"); return -EINVAL; @@ -367,7 +376,7 @@ static int bluetooth_cfg(struct bluetooth_data *data) } if (pkt->length != sizeof(struct ipc_data_cfg)) { - SNDERR("Error while configuring device: packet size doesn't" + SNDERR("Error while configuring device: packet size doesn't " "match"); ret = -EINVAL; goto done; @@ -383,7 +392,7 @@ static int bluetooth_cfg(struct bluetooth_data *data) data->cfg.sample_size, data->cfg.rate); if (data->cfg.fd == -1) { - SNDERR("Error while configuring device: could not acquire" + SNDERR("Error while configuring device: could not acquire " "audio socket"); ret = -EINVAL; goto done; -- cgit From 9217b7d4f85216946943ab588e06b6bcd2f1b5bc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 9 Jun 2007 06:26:01 +0000 Subject: Code cleanup --- audio/headset.c | 89 +++++++++++++++++++++++++++++++++------------------ audio/manager.c | 63 +++++++++++++++++++++++------------- audio/pcm_bluetooth.c | 28 ++++++++-------- audio/unix.c | 2 ++ 4 files changed, 112 insertions(+), 70 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 109e7c5d..1e1f3271 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -160,7 +160,8 @@ static void hs_signal_gain_setting(audio_device_t *device, const char *buf) DBUS_TYPE_INVALID); } -static headset_event_t parse_headset_event(const char *buf, char *rsp, int rsp_len) +static headset_event_t parse_headset_event(const char *buf, char *rsp, + int rsp_len) { printf("Received: %s\n", buf); @@ -215,7 +216,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read); + err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, + &bytes_read); if (err != G_IO_ERROR_NONE) goto failed; @@ -244,7 +246,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, memset(rsp, 0, sizeof(rsp)); - switch (parse_headset_event(&hs->buf[hs->data_start], rsp, sizeof(rsp))) { + switch (parse_headset_event(&hs->buf[hs->data_start], rsp, + sizeof(rsp))) { case HEADSET_EVENT_GAIN: hs_signal_gain_setting(device, &hs->buf[hs->data_start] + 2); break; @@ -273,9 +276,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = G_IO_ERROR_NONE; while (err == G_IO_ERROR_NONE && total_bytes_written < count) { - /* FIXME: make it async */ - err = g_io_channel_write(hs->rfcomm, rsp + total_bytes_written, - count - total_bytes_written, &bytes_written); + err = g_io_channel_write(hs->rfcomm, + rsp + total_bytes_written, + count - total_bytes_written, + &bytes_written); if (err != G_IO_ERROR_NONE) error("Error while writting to the audio output channel"); total_bytes_written += bytes_written; @@ -345,7 +349,8 @@ static void auth_callback(DBusPendingCall *call, void *data) } else { char hs_address[18]; - g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + g_io_add_watch(hs->rfcomm, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); ba2str(&device->bda, hs_address); @@ -363,7 +368,8 @@ static void auth_callback(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) +static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, + audio_device_t *device) { struct headset *hs; @@ -579,7 +585,8 @@ static int rfcomm_connect(audio_device_t *device, int *err) if (!(errno == EAGAIN || errno == EINPROGRESS)) { if (err) *err = errno; - error("connect() failed: %s (%d)", strerror(errno), errno); + error("connect() failed: %s (%d)", strerror(errno), + errno); goto failed; } @@ -604,7 +611,8 @@ failed: static int create_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t record; @@ -665,7 +673,8 @@ static int create_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) static int create_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t record; @@ -753,7 +762,8 @@ static uint32_t headset_add_ag_record(uint8_t channel, sdp_buf_t *buf) dbus_message_unref(msg); - if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { error("Adding service record failed: %s", derr.message); dbus_error_free(&derr); return 0; @@ -763,7 +773,8 @@ static uint32_t headset_add_ag_record(uint8_t channel, sdp_buf_t *buf) DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", derr.message); + error("Invalid arguments to AddServiceRecord reply: %s", + derr.message); dbus_message_unref(reply); dbus_error_free(&derr); return 0; @@ -782,7 +793,8 @@ int headset_remove_ag_record(uint32_t rec_id) DBusError derr; msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "RemoveServiceRecord"); + "org.bluez.Database", + "RemoveServiceRecord"); if (!msg) { error("Can't allocate new method call"); return 0; @@ -798,7 +810,8 @@ int headset_remove_ag_record(uint32_t rec_id) dbus_message_unref(msg); if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", rec_id, derr.message); + error("Removing service record 0x%x failed: %s", + rec_id, derr.message); dbus_error_free(&derr); return 0; } @@ -837,7 +850,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) } if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &array, &array_len, DBUS_TYPE_INVALID)) { error("Unable to get args from GetRecordReply"); goto failed_not_supported; @@ -870,15 +884,18 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != HEADSET_SVCLASS_ID) || - (uuid.type == SDP_UUID16 && uuid.value.uuid16 != HEADSET_SVCLASS_ID)) { + if ((uuid.type == SDP_UUID32 && + uuid.value.uuid32 != HEADSET_SVCLASS_ID) || + (uuid.type == SDP_UUID16 && + uuid.value.uuid16 != HEADSET_SVCLASS_ID)) { error("Service classes did not contain the expected UUID"); goto failed_not_supported; } if (!sdp_get_access_protos(record, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); - sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, + NULL); sdp_list_free(protos, NULL); protos = NULL; } @@ -937,7 +954,8 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; } - if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && hs->pending_connect) { + if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && + hs->pending_connect) { g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) err_connect_failed(connection, hs->pending_connect->msg, @@ -1011,7 +1029,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(connection, hs->pending_connect->msg, + err_connect_failed(connection, + hs->pending_connect->msg, EINTR); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -1035,7 +1054,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult hs_is_connected(DBusConnection *conn, + DBusMessage *msg, void *data) { audio_device_t *device = data; @@ -1080,7 +1100,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); if (c->msg) { - if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) + if (dbus_error_has_name(&derr, + "org.bluez.Error.ConnectionAttemptFailed")) err_connect_failed(connection, c->msg, EHOSTDOWN); else err_not_supported(connection, c->msg); @@ -1090,7 +1111,8 @@ static void get_handles_reply(DBusPendingCall *call, void *data) } if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &array, &array_len, DBUS_TYPE_INVALID)) { error("Unable to get args from reply"); @@ -1173,7 +1195,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, hs->pending_connect = g_try_new0(struct pending_connect, 1); if (!hs->pending_connect) { - error("Out of memory when allocating new struct pending_connect"); + error("Out of memory when allocating struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -1290,7 +1312,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, } if (hs->ring_timer) { - debug("Got Ring method call while ringing already in progress"); + debug("IndicateCall received when already indicating"); goto done; } @@ -1308,7 +1330,8 @@ done: return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, + DBusMessage *msg, void *data) { audio_device_t *device = data; @@ -1350,7 +1373,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, int sk, err; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); /* FIXME: in progress error? */ + return err_not_connected(connection, msg); if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) return err_already_connected(connection, msg); @@ -1580,7 +1603,8 @@ void headset_free(const char *object_path) device->headset = NULL; } -static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, + void *data) { int srv_sk, cli_sk; struct sockaddr_rc addr; @@ -1640,10 +1664,11 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * uuid = HFP_AG_UUID; } - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", "RequestAuthorization"); if (!auth) { - error("Unable to allocat new RequestAuthorization method call"); + error("Unable to allocate RequestAuthorization method call"); goto failed; } @@ -1652,7 +1677,7 @@ static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, void * dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - if (dbus_connection_send_with_reply(connection, auth, &pending, -1) == FALSE) { + if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { error("Sending of authorization request failed"); goto failed; } diff --git a/audio/manager.c b/audio/manager.c index 18a653da..c0b51976 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -162,7 +162,8 @@ static audio_device_t *find_device(bdaddr_t *bda) return NULL; } -static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult device_get_address(DBusConnection *conn, + DBusMessage *msg, void *data) { audio_device_t *device = data; @@ -250,13 +251,15 @@ static void remove_device(audio_device_t *device) static gboolean add_device(audio_device_t *device) { - if (!dbus_connection_create_object_path(connection, device->object_path, + if (!dbus_connection_create_object_path(connection, + device->object_path, device, NULL)) { error("D-Bus failed to register %s path", device->object_path); return FALSE; } - if (!dbus_connection_register_interface(connection, device->object_path, + if (!dbus_connection_register_interface(connection, + device->object_path, AUDIO_DEVICE_INTERFACE, device_methods, NULL, NULL)) { error("Failed to register %s interface to %s", @@ -330,7 +333,8 @@ void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) dbus_message_unref(msg); - if (dbus_error_is_set(&derr) || dbus_set_error_from_message(&derr, reply)) { + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { error("FinishRemoteServiceTransaction(%s) failed: %s", address, derr.message); dbus_error_free(&derr); @@ -638,7 +642,8 @@ static void get_handles_reply(DBusPendingCall *call, } if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &array, &array_len, DBUS_TYPE_INVALID)) { err_failed(connection, data->msg, @@ -830,7 +835,8 @@ static gboolean device_matches(audio_device_t *device, char **interfaces) return TRUE; } -static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_create_device(DBusConnection *conn, + DBusMessage *msg, void *data) { const char *address, *path; @@ -966,16 +972,16 @@ static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *ms "Unable to create new audio device"); } - created = TRUE; - if (!device->headset) { device->headset = headset_init(device->object_path, NULL, 0); - if (!device->headset) { - remove_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to init Headset interface"); - } + created = TRUE; + } + + if (!device->headset) { + remove_device(device); + return error_reply(connection, msg, + "org.bluez.audio.Error.Failed", + "Unable to init Headset interface"); } done: @@ -1087,7 +1093,8 @@ static DBusHandlerResult am_remove_headset(DBusConnection *conn, return am_remove_device(conn, msg, data); } -static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_list_headsets(DBusConnection *conn, + DBusMessage *msg, void *data) { DBusMessageIter iter; @@ -1122,7 +1129,8 @@ static DBusHandlerResult am_list_headsets(DBusConnection *conn, DBusMessage *msg return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_find_by_addr(DBusConnection *conn, + DBusMessage *msg, void *data) { const char *path, *address; @@ -1160,7 +1168,8 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBusMessage *msg, return send_message_and_unref(conn, reply); } -static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_get_default_headset(DBusConnection *conn, + DBusMessage *msg, void *data) { DBusMessage *reply; @@ -1183,7 +1192,8 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, DBusMessag return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMessage *msg, +static DBusHandlerResult am_change_default_headset(DBusConnection *conn, + DBusMessage *msg, void *data) { DBusError derr; @@ -1295,11 +1305,18 @@ void audio_exit(void) int manager_get_device(uint8_t role, struct ipc_data_cfg *cfg) { - if (default_hs == NULL || default_hs->headset == NULL) - return -1; + GSList *l; - if (!headset_is_connected(default_hs->headset)) - return -1; + if (default_hs && default_hs->headset && + headset_is_connected(default_hs->headset)) + return headset_get_config(default_hs->headset, cfg); + + for (l = devices; l != NULL; l = l->next) { + audio_device_t *dev = l->data; + + if (dev->headset && headset_is_connected(dev->headset)) + return headset_get_config(dev->headset, cfg); + } - return headset_get_config(default_hs->headset, cfg); + return -1; } diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index f2d3c275..26b7ad2c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -57,14 +57,14 @@ struct bluetooth_data { static int bluetooth_start(snd_pcm_ioplug_t *io) { - DBG("io %p", io); + DBG("bluetooth_start %p", io); return 0; } static int bluetooth_stop(snd_pcm_ioplug_t *io) { - DBG("io %p", io); + DBG("bluetooth_stop %p", io); return 0; } @@ -73,7 +73,7 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - DBG("io %p", io); + DBG("bluetooth_pointer %p", io); DBG("hw_ptr=%lu", data->hw_ptr); @@ -84,14 +84,13 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - DBG("io %p", io); + DBG("bluetooth_close %p", io); free(data); return 0; } - static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -99,16 +98,15 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); - if (io->stream == SND_PCM_STREAM_PLAYBACK) { + if (io->stream == SND_PCM_STREAM_PLAYBACK) /* If not null for playback, xmms doesn't display time * correctly */ data->hw_ptr = 0; - } - else { + else /* ALSA library is really picky on the fact hw_ptr is not null. * If it is, capture won't start */ data->hw_ptr = io->period_size; - } + return 0; } @@ -118,22 +116,22 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params struct ipc_data_cfg cfg = data->cfg; uint32_t period_count = io->buffer_size / io->period_size; int opt_name; + + DBG("fd = %d, period_count = %d", cfg.fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; - DBG("fd = %d, period_count = %d", cfg.fd, period_count); - if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) + sizeof(period_count)) == 0) return 0; - + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDBUF : SO_RCVBUF; + SO_SNDBUF : SO_RCVBUF; if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) - return 0; + return 0; /* backward compatible to the old patch */ diff --git a/audio/unix.c b/audio/unix.c index f2bf2271..76650b75 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -85,9 +85,11 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) pkt->type = PKT_TYPE_CFG_RSP; pkt->length = sizeof(struct ipc_data_cfg); pkt->error = PKT_ERROR_NONE; + len = sendto(sk, pkt, len, 0, (struct sockaddr *) &addr, addrlen); if (len < 0) info("Error %s(%d)", strerror(errno), errno); + info("%d bytes sent", len); break; case PKT_TYPE_STATUS_REQ: -- cgit From 82a2b31aea724dd0a051e9cda3fb51e018e8b606 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 11 Jun 2007 07:42:56 +0000 Subject: Add audio service test script --- audio/Makefile.am | 2 +- audio/test-audio | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100755 audio/test-audio (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 2285ed7f..731acb3a 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -35,6 +35,6 @@ AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS INCLUDES = -I$(top_srcdir)/common -EXTRA_DIST = audio.service audio.conf audio-api.txt asound.conf +EXTRA_DIST = audio.service audio.conf audio-api.txt test-audio asound.conf MAINTAINERCLEANFILES = Makefile.in diff --git a/audio/test-audio b/audio/test-audio new file mode 100755 index 00000000..5553f0a5 --- /dev/null +++ b/audio/test-audio @@ -0,0 +1,19 @@ +#!/usr/bin/python + +import dbus + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), + 'org.bluez.Manager') + +conn = manager.ActivateService('audio') + +audio = dbus.Interface(bus.get_object(conn, '/org/bluez/audio'), + 'org.bluez.audio.Manager') + +try: + headset = dbus.Interface(bus.get_object(conn, audio.DefaultHeadset()), + 'org.bluez.audio.Headset') +except: + pass -- cgit From 1ec7d98bba8eaf18c7024123d7b9196f4bf7aefc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 11 Jun 2007 12:16:51 +0000 Subject: Remove ring timer when a headset gets disconnected --- audio/headset.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1e1f3271..ae7a32cc 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1019,6 +1019,11 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (hs->state > HEADSET_STATE_CONNECTED) hs_stop(NULL, NULL, device); + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } + if (hs->rfcomm) { g_io_channel_close(hs->rfcomm); g_io_channel_unref(hs->rfcomm); -- cgit From ddf8edc54d666f9b6f75f28b6db5375e2f0982a8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 11 Jun 2007 23:26:24 +0000 Subject: Fix file descriptor passing. --- audio/headset.c | 5 +++- audio/ipc.h | 2 +- audio/pcm_bluetooth.c | 79 +++++++++++++++++++++++++++++++++++++-------------- audio/unix.c | 79 +++++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 127 insertions(+), 38 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index ae7a32cc..2d398324 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -421,6 +421,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, debug("SCO socket opened for headset %s", device->object_path); + info("SCO fd=%d", sk); hs->sco = chan; hs->pending_connect->io = NULL; @@ -435,6 +436,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; + fcntl(sk, F_SETFL, 0); + hs->state = HEADSET_STATE_PLAYING; dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, @@ -1436,7 +1439,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, } debug("SCO connect in progress"); - g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, + g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, (GIOFunc) sco_connect_cb, device); } else { debug("SCO connect succeeded with first try"); diff --git a/audio/ipc.h b/audio/ipc.h index 0cd9e620..409abfda 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -25,7 +25,7 @@ #define IPC_TYPE_CONNECT 0x0001 -#define IPC_SOCKET_NAME "/org/bluez/audio" +#define IPC_SOCKET_NAME "\0/org/bluez/audio" #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 26b7ad2c..10182993 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -118,7 +118,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params int opt_name; DBG("fd = %d, period_count = %d", cfg.fd, period_count); - + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; @@ -142,6 +142,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params sizeof(period_count)) == 0) return 0; + SNDERR("%s (%d)", strerror(errno), errno); SNDERR("Unable to set number of SCO buffers: please upgrade your " "kernel!"); @@ -318,6 +319,51 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return 0; } +static int bluetooth_recvmsg_fd(struct bluetooth_data *data) +{ + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct ipc_packet pkt; + int err, ret; + struct iovec iov = { + .iov_base = &pkt, + .iov_len = sizeof(pkt) + }; + struct msghdr msgh = { + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &cmsg_b, + .msg_controllen = CMSG_LEN(sizeof(int)), + .msg_flags = 0 + }; + + ret = recvmsg(data->sock, &msgh, 0); + + if (ret < 0) { + err = errno; + SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); + return -err; + } + + if(pkt.type == PKT_TYPE_CFG_RSP) { + struct cmsghdr *cmsg; + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) + data->cfg.fd = (*(int *) CMSG_DATA(cmsg)); + DBG("fd = %d", data->cfg.fd); + return 0; + } + } + else + SNDERR("Unexpected packet type received: type = %d", pkt.type); + + return -EINVAL; +} + static int bluetooth_cfg(struct bluetooth_data *data) { int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); @@ -396,7 +442,13 @@ static int bluetooth_cfg(struct bluetooth_data *data) goto done; } - ret = 0; + if ((ret = bluetooth_recvmsg_fd(data)) < 0) + goto done; + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + while(recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, + MSG_DONTWAIT) > 0); done: free(pkt); @@ -406,7 +458,9 @@ done: static int bluetooth_init(struct bluetooth_data *data) { int sk, err, id; - struct sockaddr_un addr; + struct sockaddr_un addr = { + AF_UNIX, IPC_SOCKET_NAME + }; if (!data) return -EINVAL; @@ -417,30 +471,13 @@ static int bluetooth_init(struct bluetooth_data *data) id = abs(getpid() * rand()); - sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + sk = socket(PF_LOCAL, SOCK_STREAM, 0); if (sk < 0) { err = -errno; SNDERR("Can't open socket"); return -errno; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", - IPC_SOCKET_NAME, id); - - DBG("Binding address: %s", addr.sun_path + 1); - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - SNDERR("Can't bind socket"); - close(sk); - return err; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); - DBG("Connecting to address: %s", addr.sun_path + 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = -errno; diff --git a/audio/unix.c b/audio/unix.c index 76650b75..00f146be 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -26,13 +26,13 @@ #endif #include +#include +#include #include #include #include #include #include -#include -#include #include @@ -44,13 +44,46 @@ static int unix_sock = -1; +/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX) +and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set +to SCM_RIGHTS and the data being an integer value equal to the handle of the +file descriptor to be passed.*/ +static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) +{ + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct iovec iov = { + .iov_base = pkt, + .iov_len = sizeof(struct ipc_packet) + }; + + struct msghdr msgh = { + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &cmsg_b, + .msg_controllen = CMSG_LEN(sizeof(int)), + .msg_flags = 0 + }; + + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + /* Initialize the payload */ + (*(int *)CMSG_DATA(cmsg)) = fd; + + return sendmsg(sock, &msgh, MSG_NOSIGNAL); +} + static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; socklen_t addrlen; struct ipc_packet *pkt; struct ipc_data_cfg *cfg; - int sk, len; + int sk, clisk, len; debug("chan %p cond %td data %p", chan, cond, data); @@ -69,7 +102,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); pkt = g_malloc0(len); - len = recvfrom(sk, pkt, len, 0, (struct sockaddr *) &addr, &addrlen); + clisk = accept(sk, (struct sockaddr *) &addr, &addrlen); + len = recv(clisk, pkt, len, 0); debug("path %s len %d", addr.sun_path + 1, len); @@ -79,18 +113,30 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) cfg = (struct ipc_data_cfg *) pkt->data; + memset(cfg, 0, sizeof(struct ipc_data_cfg)); if (manager_get_device(pkt->role, cfg) < 0) cfg->fd = -1; + info("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," + "rate=%u", cfg->fd, cfg->fd_opt, cfg->channels, + cfg->pkt_len, cfg->sample_size, cfg->rate); + pkt->type = PKT_TYPE_CFG_RSP; pkt->length = sizeof(struct ipc_data_cfg); pkt->error = PKT_ERROR_NONE; - len = sendto(sk, pkt, len, 0, (struct sockaddr *) &addr, addrlen); + len = send(clisk, pkt, len, 0); if (len < 0) info("Error %s(%d)", strerror(errno), errno); - info("%d bytes sent", len); + + if (cfg->fd != -1) { + len = unix_sendmsg_fd(clisk, cfg->fd, pkt); + if (len < 0) + info("Error %s(%d)", strerror(errno), errno); + info("%d bytes sent", len); + } + break; case PKT_TYPE_STATUS_REQ: info("Package PKT_TYPE_STATUS_REQ"); @@ -101,25 +147,26 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) } g_free(pkt); + close(clisk); return TRUE; } int unix_init(void) { GIOChannel *io; - struct sockaddr_un addr; - int sk; + struct sockaddr_un addr = { + AF_UNIX, IPC_SOCKET_NAME + }; + + int sk, err; - sk = socket(PF_LOCAL, SOCK_DGRAM, 0); + sk = socket(PF_LOCAL, SOCK_STREAM, 0); if (sk < 0) { - error("Can't create unix socket: %s (%d)", strerror(errno), errno); - return -1; + err = errno; + error("Can't create unix socket: %s (%d)", strerror(err), err); + return -err; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { error("Can't bind unix socket: %s (%d)", strerror(errno), errno); close(sk); @@ -130,6 +177,8 @@ int unix_init(void) unix_sock = sk; + listen(sk, 1); + io = g_io_channel_unix_new(sk); g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, -- cgit From c1fe207c205d3122a496b11d36132940e34568a4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 12 Jun 2007 06:39:40 +0000 Subject: Fix triggering an assert if the SCO connect callback gets triggered on the same mainloop iteration as the RFCOMM disconnect callback. --- audio/headset.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 2d398324..d6cc7751 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -85,6 +85,7 @@ typedef enum { struct pending_connect { DBusMessage *msg; GIOChannel *io; + guint io_id; }; struct headset { @@ -1036,6 +1037,8 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (hs->pending_connect) { if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); + if (hs->pending_connect->io_id) + g_source_remove(hs->pending_connect->io_id); if (hs->pending_connect->msg) err_connect_failed(connection, hs->pending_connect->msg, @@ -1439,8 +1442,9 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, } debug("SCO connect in progress"); - g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, device); + c->io_id = g_io_add_watch(c->io, + G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + (GIOFunc) sco_connect_cb, device); } else { debug("SCO connect succeeded with first try"); do_callback = TRUE; -- cgit From e703434f83af5a41cb2f27604aedc8b2d15024e2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 12 Jun 2007 06:43:16 +0000 Subject: Cleanup --- audio/pcm_bluetooth.c | 7 ++++--- audio/unix.c | 10 ++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 10182993..42c1e557 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -346,11 +346,11 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) return -err; } - if(pkt.type == PKT_TYPE_CFG_RSP) { + if (pkt.type == PKT_TYPE_CFG_RSP) { struct cmsghdr *cmsg; /* Receive auxiliary data in msgh */ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) data->cfg.fd = (*(int *) CMSG_DATA(cmsg)); @@ -442,7 +442,8 @@ static int bluetooth_cfg(struct bluetooth_data *data) goto done; } - if ((ret = bluetooth_recvmsg_fd(data)) < 0) + ret = bluetooth_recvmsg_fd(data); + if (ret < 0) goto done; /* It is possible there is some outstanding diff --git a/audio/unix.c b/audio/unix.c index 00f146be..163f4f98 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -72,7 +72,7 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); /* Initialize the payload */ - (*(int *)CMSG_DATA(cmsg)) = fd; + (*(int *) CMSG_DATA(cmsg)) = fd; return sendmsg(sock, &msgh, MSG_NOSIGNAL); } @@ -100,9 +100,14 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); + clisk = accept(sk, (struct sockaddr *) &addr, &addrlen); + if (clisk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); pkt = g_malloc0(len); - clisk = accept(sk, (struct sockaddr *) &addr, &addrlen); len = recv(clisk, pkt, len, 0); debug("path %s len %d", addr.sun_path + 1, len); @@ -128,6 +133,7 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) len = send(clisk, pkt, len, 0); if (len < 0) info("Error %s(%d)", strerror(errno), errno); + info("%d bytes sent", len); if (cfg->fd != -1) { -- cgit From 645b2dbd1f6071102e4cc18c2ff32a236883ff76 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 12 Jun 2007 08:36:06 +0000 Subject: Mark some methods and signals as experimental --- audio/audio-api.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 175f62f3..355f4cfe 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -12,7 +12,7 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio Methods - string CreateDevice(string address, array{string} interfaces) + string CreateDevice(string address, array{string} interfaces) [experimental] Creates a new audio device object. If not yet done, this method will perform a SDP query on the remote @@ -24,12 +24,12 @@ Methods device does not support all of the interfaces listed in the interfaces parameter. - void RemoveDevice(string path) + void RemoveDevice(string path) [experimental] Removes a device from the device tree. If there are any connections open to the device they will be closed. - array{string} ListDevices(array{string} interfaces) + array{string} ListDevices(array{string} interfaces) [experimental] Retuns an array of strings indicating the object paths of available devices which implement as a minimum the @@ -39,7 +39,7 @@ Methods Returns list of headset objects that are configured. - string FindDeviceByAddress(string address) + string FindDeviceByAddress(string address) [experimental] Searches the list of available devices and returns the object path of the first device which matchess address. @@ -64,11 +64,11 @@ Methods related to it. Signals - void DeviceCreated(string path) + void DeviceCreated(string path) [experimental] Sent when a new device object has been created. - void DeviceRemoved(string path) + void DeviceRemoved(string path) [experimental] Sent when a device object has been removed. @@ -90,7 +90,7 @@ org.bluez.audio.Device interface Object path(s) /org/bluez/audio/device* -Methods string GetAddress() +Methods string GetAddress() [experimental] Returns the Bluetooth address of the remote device. -- cgit From d983837c15005d162b04fe02f628afdcf0fc2a8b Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Tue, 12 Jun 2007 13:49:28 +0000 Subject: take out extra setsockopt --- audio/pcm_bluetooth.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 42c1e557..a313eab8 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -133,15 +133,6 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params sizeof(period_count)) == 0) return 0; - /* backward compatible to the old patch */ - - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SCO_TXBUFS : SCO_RXBUFS; - - if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) - return 0; - SNDERR("%s (%d)", strerror(errno), errno); SNDERR("Unable to set number of SCO buffers: please upgrade your " "kernel!"); -- cgit From c6136c57053db24e9c4e2448f25bd46f6a90b053 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 12 Jun 2007 14:58:03 +0000 Subject: Some fixes for alsa plugin. --- audio/pcm_bluetooth.c | 68 ++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a313eab8..491e621a 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -86,6 +86,7 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) DBG("bluetooth_close %p", io); + free(data->buffer); free(data); return 0; @@ -115,7 +116,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; uint32_t period_count = io->buffer_size / io->period_size; - int opt_name; + int opt_name, err; DBG("fd = %d, period_count = %d", cfg.fd, period_count); @@ -133,11 +134,10 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params sizeof(period_count)) == 0) return 0; - SNDERR("%s (%d)", strerror(errno), errno); - SNDERR("Unable to set number of SCO buffers: please upgrade your " - "kernel!"); + err = errno; + SNDERR("%s (%d)", strerror(err), err); - return -EINVAL; + return -err; } static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, @@ -203,7 +203,7 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, struct ipc_data_cfg cfg = data->cfg; snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; - unsigned char *buff; + uint8_t *buff; int rsend; DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," @@ -215,8 +215,10 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, else frames_to_read = (cfg.pkt_len - data->count) / cfg.sample_size; + DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); + /* Ready for more data */ - buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read); if ((data->count + areas->step / 8 * frames_to_read) != cfg.pkt_len) { @@ -226,13 +228,15 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, goto done; } - rsend = send(cfg.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); + rsend = send(cfg.fd, data->buffer, cfg.pkt_len, + io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ data->count = 0; /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + data->hw_ptr = (data->hw_ptr + frames_to_read / cfg.sample_size) + % io->buffer_size; ret = frames_to_read; } else if (rsend < 0) @@ -269,6 +273,8 @@ static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) { + struct bluetooth_data *data = io->private_data; + struct ipc_data_cfg cfg = data->cfg; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, /* Mmap access is really useless fo this driver, but we @@ -281,29 +287,36 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) }; int err; + /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, ARRAY_NELEMS(access_list), access_list); if (err < 0) return err; + /* supported formats */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, ARRAY_NELEMS(format_list), format_list); if (err < 0) return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, 1, 1); + /* supported channels */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, + cfg.channels, cfg.channels); if (err < 0) return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 8000); + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, + cfg.rate, cfg.rate); if (err < 0) return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, 48, 48); + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + cfg.pkt_len, cfg.pkt_len); if (err < 0) return err; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 200); + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, + 2, 200); if (err < 0) return err; @@ -362,8 +375,7 @@ static int bluetooth_cfg(struct bluetooth_data *data) DBG("Sending PKT_TYPE_CFG_REQ..."); - pkt = malloc(len); - if (!pkt) + if ((pkt = malloc(len)) == 0) return -ENOMEM; memset(pkt, 0, len); @@ -371,8 +383,7 @@ static int bluetooth_cfg(struct bluetooth_data *data) pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; - ret = send(data->sock, pkt, len, 0); - if (ret < 0) { + if ((ret = send(data->sock, pkt, len, 0)) < 0) { ret = -errno; goto done; } else if (ret == 0) { @@ -385,8 +396,7 @@ static int bluetooth_cfg(struct bluetooth_data *data) DBG("Waiting for response..."); memset(pkt, 0, len); - ret = recv(data->sock, pkt, len, 0); - if (ret < 0) { + if ((ret = recv(data->sock, pkt, len, 0)) < 0) { ret = -errno; goto done; } else if (ret == 0) { @@ -433,15 +443,19 @@ static int bluetooth_cfg(struct bluetooth_data *data) goto done; } - ret = bluetooth_recvmsg_fd(data); - if (ret < 0) + if ((ret = bluetooth_recvmsg_fd(data)) < 0) goto done; + if ((data->buffer = malloc(data->cfg.pkt_len)) == 0) + return -ENOMEM; + /* It is possible there is some outstanding data in the pipe - we have to empty it */ while(recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, MSG_DONTWAIT) > 0); + memset(data->buffer, 0, data->cfg.pkt_len); + done: free(pkt); return ret; @@ -463,8 +477,7 @@ static int bluetooth_init(struct bluetooth_data *data) id = abs(getpid() * rand()); - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { + if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { err = -errno; SNDERR("Can't open socket"); return -errno; @@ -480,16 +493,9 @@ static int bluetooth_init(struct bluetooth_data *data) data->sock = sk; - err = bluetooth_cfg(data); - if (err < 0) + if ((err = bluetooth_cfg(data)) < 0) return err; - data->buffer = malloc(data->cfg.pkt_len); - if (!data->buffer) - return -ENOMEM; - - memset(data->buffer, 0, data->cfg.pkt_len); - return 0; } -- cgit From 35b7eb29eebf1a85fce60aa0b946ff2e04ebf684 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 12 Jun 2007 23:02:30 +0000 Subject: Correct block size in alsa plugin. --- audio/pcm_bluetooth.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 491e621a..6b2167e9 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -204,26 +204,27 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; uint8_t *buff; - int rsend; + int rsend, block_size; DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," "io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); - if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) + block_size = areas->step / 8; + if ((data->count + block_size * size) <= cfg.pkt_len) frames_to_read = size; else - frames_to_read = (cfg.pkt_len - data->count) / cfg.sample_size; + frames_to_read = (cfg.pkt_len - data->count) / block_size; DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); /* Ready for more data */ buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; - memcpy(data->buffer + data->count, buff, areas->step / 8 * frames_to_read); + memcpy(data->buffer + data->count, buff, block_size * frames_to_read); - if ((data->count + areas->step / 8 * frames_to_read) != cfg.pkt_len) { + if ((data->count + block_size * frames_to_read) != cfg.pkt_len) { /* Remember we have some frame in the pipe now */ - data->count += areas->step / 8 * frames_to_read; + data->count += block_size * frames_to_read; ret = frames_to_read; goto done; } @@ -235,7 +236,7 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, data->count = 0; /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + frames_to_read / cfg.sample_size) + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / block_size) % io->buffer_size; ret = frames_to_read; @@ -305,11 +306,13 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; + /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, cfg.rate, cfg.rate); if (err < 0) return err; + /* supported block size */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, cfg.pkt_len, cfg.pkt_len); if (err < 0) -- cgit From 3f171de3179effc5b22462497bcbd56b18887adc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 13 Jun 2007 06:23:08 +0000 Subject: Refuse to pass SCO socket to alsa if PCM SCO routing is configured --- audio/headset.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index d6cc7751..6e5ec517 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -113,6 +113,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data); static gboolean disable_hfp = FALSE; +static gboolean sco_over_hci = TRUE; static GIOChannel *hs_server = NULL; static GIOChannel *hf_server = NULL; @@ -1800,6 +1801,8 @@ int headset_server_init(DBusConnection *conn, gboolean no_hfp, disable_hfp = no_hfp; + sco_over_hci = sco_hci; + if (disable_hfp) return 0; @@ -1857,6 +1860,9 @@ void headset_exit(void) int headset_get_config(headset_t *headset, struct ipc_data_cfg *cfg) { + if (!sco_over_hci) + return -1; + if (headset->sco == NULL) return -1; -- cgit From 9dc2cfaca68df6b7aa9824da20cfe2eb01980f73 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 13 Jun 2007 06:47:32 +0000 Subject: Set default config values to what currently works --- audio/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 796af3b5..3a170b1a 100644 --- a/audio/main.c +++ b/audio/main.c @@ -41,8 +41,8 @@ #include "headset.h" /* Configuration settings */ -static gboolean disable_hfp = FALSE; -static gboolean sco_hci = TRUE; +static gboolean disable_hfp = TRUE; +static gboolean sco_hci = FALSE; static GMainLoop *main_loop = NULL; -- cgit From e5b89a347b5da0bec360495f78715b772a849e3c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 13 Jun 2007 07:27:53 +0000 Subject: Don't install audio.conf file --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 731acb3a..68648a16 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -3,7 +3,7 @@ if AUDIOSERVICE if CONFIGFILES confdir = $(sysconfdir)/bluetooth -conf_DATA = audio.service audio.conf +conf_DATA = audio.service endif servicedir = $(libdir)/bluetooth -- cgit From f6ca761e65a9318e0173ade27501fb41604ee14c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 13 Jun 2007 21:58:30 +0000 Subject: Code cleanup. --- audio/pcm_bluetooth.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6b2167e9..d588bcfd 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -36,7 +36,11 @@ #include "ipc.h" +#ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) +#else +#define DBG(fmt, arg...) +#endif #ifndef SCO_TXBUFS #define SCO_TXBUFS 0x03 @@ -149,7 +153,7 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, struct ipc_data_cfg cfg = data->cfg; snd_pcm_uframes_t frames_to_write, ret; unsigned char *buff; - int nrecv; + int nrecv, frame_size = 0; DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); @@ -157,6 +161,8 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, if (data->count > 0) goto proceed; + frame_size = areas->step / 8; + nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); @@ -180,10 +186,10 @@ proceed: if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) frames_to_write = size; else - frames_to_write = (cfg.pkt_len - data->count) / cfg.sample_size; + frames_to_write = (cfg.pkt_len - data->count) / frame_size; - memcpy(buff, data->buffer + data->count, areas->step / 8 * frames_to_write); - data->count += (areas->step / 8 * frames_to_write); + memcpy(buff, data->buffer + data->count, frame_size * frames_to_write); + data->count += (frame_size * frames_to_write); data->count %= cfg.pkt_len; /* Return written frames count */ @@ -204,27 +210,27 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; uint8_t *buff; - int rsend, block_size; + int rsend, frame_size; DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," "io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); - block_size = areas->step / 8; - if ((data->count + block_size * size) <= cfg.pkt_len) + frame_size = areas->step / 8; + if ((data->count + size * frame_size) <= cfg.pkt_len) frames_to_read = size; else - frames_to_read = (cfg.pkt_len - data->count) / block_size; + frames_to_read = (cfg.pkt_len - data->count) / frame_size; DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); /* Ready for more data */ buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; - memcpy(data->buffer + data->count, buff, block_size * frames_to_read); + memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); - if ((data->count + block_size * frames_to_read) != cfg.pkt_len) { + if ((data->count + frames_to_read * frame_size) != cfg.pkt_len) { /* Remember we have some frame in the pipe now */ - data->count += block_size * frames_to_read; + data->count += frames_to_read * frame_size; ret = frames_to_read; goto done; } @@ -236,7 +242,7 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, data->count = 0; /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / block_size) + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / frame_size) % io->buffer_size; ret = frames_to_read; -- cgit From 5ed2a3ba745856206c66d8ecb98afb8a1f9ec7b5 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 14 Jun 2007 20:36:26 +0000 Subject: Initial support for audio control plugin and some code cleanups. --- audio/ctl_bluetooth.c | 222 +++++++++++++++++++++++++++++++++++--------------- audio/ipc.h | 34 ++++---- audio/pcm_bluetooth.c | 24 ++++-- 3 files changed, 195 insertions(+), 85 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 4d73c788..411d7e00 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -33,7 +33,11 @@ #include "ipc.h" +#ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) +#else +#define DBG(fmt, arg...) +#endif #define BLUETOOTH_MINVOL 0 #define BLUETOOTH_MAXVOL 15 @@ -53,15 +57,24 @@ static const char *vol_devices[2] = { [BLUETOOTH_CAPTURE] = "Capture volume", }; +static void bluetooth_exit(struct bluetooth_data *data) +{ + if (data == NULL) + return; + + if (data->sock >= 0) + close(data->sock); + + free(data); +} + static void bluetooth_close(snd_ctl_ext_t *ext) { struct bluetooth_data *data = ext->private_data; DBG("ext %p", ext); - close(data->sock); - - free(data); + bluetooth_exit(data); } static int bluetooth_elem_count(snd_ctl_ext_t *ext) @@ -125,48 +138,140 @@ static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, return 0; } +static int bluetooth_send_ctl(struct bluetooth_data *data, + struct ipc_packet *pkt, int len) +{ + int ret; + + ret = send(data->sock, pkt, len, MSG_NOSIGNAL); + if (ret <= 0) { + SYSERR("Unable to request new volume value to server"); + return -errno; + } + + ret = recv(data->sock, pkt, len, 0); + if (ret <= 0) { + SYSERR("Unable to receive new volume value from server"); + return -errno; + } + + if(pkt->type != PKT_TYPE_CTL_RSP) { + SNDERR("Unexpected packet type %d received", pkt->type); + return -EINVAL; + } + + if(pkt->length != sizeof(struct ipc_data_ctl)) { + SNDERR("Unexpected packet length %d received", pkt->length); + return -EINVAL; + } + + return 0; +} + static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - unsigned char buf[] = { 0x00, 0x00 }; - int len; + struct ipc_packet *pkt; + struct ipc_data_ctl *ctl; + int len, ret; DBG("ext %p key %ld", ext, key); - len = write(data->sock, buf, sizeof(buf)); - + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); + pkt = malloc(len); + memset(pkt, 0, len); *value = 0; - return 0; + pkt->type = PKT_TYPE_CTL_REQ; + pkt->length = sizeof(struct ipc_data_ctl); + ctl = (struct ipc_data_ctl *) pkt->data; + ctl->mode = key; + + if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + goto done; + + *value = ctl->key; +done: + free(pkt); + return ret; } static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - unsigned char buf[] = { 0xff, 0xff }; - int len; + struct ipc_packet *pkt; + struct ipc_data_ctl *ctl; + long current; + int len, ret; DBG("ext %p key %ld", ext, key); - len = write(data->sock, buf, sizeof(buf)); + if ((ret = bluetooth_read_integer(ext, key, ¤t)) < 0) + return ret; - return 0; + if (*value == current) + return 0; + + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); + pkt = malloc(len); + memset(pkt, 0, len); + + pkt->length = sizeof(struct ipc_data_ctl); + ctl = (struct ipc_data_ctl *) pkt->data; + ctl->mode = key; + + while (*value != current) { + pkt->type = PKT_TYPE_CTL_REQ; + ctl->key = (*value > current) ? CTL_KEY_VOL_UP : CTL_KEY_VOL_DOWN; + + if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + break; + + current = ctl->key; + } + + free(pkt); + return ret; } static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { struct bluetooth_data *data = ext->private_data; - unsigned char buf[128]; - int len; + struct ipc_packet *pkt; + struct ipc_data_ctl *ctl; + int len, ret; - //DBG("ext %p id %p", ext, id); + DBG("ext %p id %p", ext, id); - len = recv(data->sock, buf, sizeof(buf), MSG_DONTWAIT); + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); + pkt = malloc(len); + memset(pkt, 0, len); - return 0; + ret = recv(data->sock, pkt, len, MSG_DONTWAIT); + if (ret <= 0) + return -errno; + + if(pkt->type != PKT_TYPE_CTL_NTFY) { + SNDERR("Unexpected packet type %d received!", pkt->type); + return -EAGAIN; + } + + if(pkt->length != sizeof(struct ipc_data_ctl)) { + SNDERR("Unexpected packet length %d received", pkt->length); + return -EAGAIN; + } + + ctl = (struct ipc_data_ctl *) pkt->data; + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, ctl->mode == BLUETOOTH_PLAYBACK ? + vol_devices[BLUETOOTH_PLAYBACK] : + vol_devices[BLUETOOTH_CAPTURE]); + *event_mask = SND_CTL_EVENT_MASK_VALUE; + + return 1; } static snd_ctl_ext_callback_t bluetooth_callback = { @@ -181,69 +286,60 @@ static snd_ctl_ext_callback_t bluetooth_callback = { .read_event = bluetooth_read_event, }; -SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) +static int bluetooth_init(struct bluetooth_data *data) { - snd_config_iterator_t iter, next; - struct bluetooth_data *data; - struct sockaddr_un addr; - unsigned int id; - int sk, err; - - DBG("Bluetooth Control plugin"); - - snd_config_for_each(iter, next, conf) { - snd_config_t *n = snd_config_iterator_entry(iter); - const char *id; - - if (snd_config_get_id(n, &id) < 0) - continue; + int sk, err, id; + struct sockaddr_un addr = { + AF_UNIX, IPC_SOCKET_NAME + }; - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) - continue; + if (!data) + return -EINVAL; - SNDERR("Unknown field %s", id); + memset(data, 0, sizeof(struct bluetooth_data)); - return -EINVAL; - } + data->sock = -1; id = abs(getpid() * rand()); - sk = socket(PF_LOCAL, SOCK_DGRAM, 0); - if (sk < 0) { + if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { + err = -errno; SNDERR("Can't open socket"); return -errno; } - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s/%d", - IPC_SOCKET_NAME, id); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - SNDERR("Can't bind socket"); - close(sk); - return -errno; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - snprintf(addr.sun_path + 1, UNIX_PATH_MAX - 2, "%s", IPC_SOCKET_NAME); - + DBG("Connecting to address: %s", addr.sun_path + 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; SNDERR("Can't connect socket"); close(sk); - return -errno; + return err; } - data = malloc(sizeof(*data)); + data->sock = sk; + + return 0; +} + +SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) +{ + struct bluetooth_data *data; + int err; + + DBG("Bluetooth Control plugin"); + + data = malloc(sizeof(struct bluetooth_data)); + memset(data, 0, sizeof(struct bluetooth_data)); if (!data) { - close(sk); - return -ENOMEM; + err = -ENOMEM; + goto error; } - memset(data, 0, sizeof(*data)); + err = bluetooth_init(data); + if (err < 0) + goto error; - data->sock = sk; + memset(data, 0, sizeof(*data)); data->ext.version = SND_CTL_EXT_VERSION; data->ext.card_idx = -1; @@ -255,7 +351,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1); data->ext.callback = &bluetooth_callback; - data->ext.poll_fd = sk; + data->ext.poll_fd = data->sock; data->ext.private_data = data; err = snd_ctl_ext_create(&data->ext, name, mode); @@ -267,9 +363,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) return 0; error: - close(sk); - - free(data); + bluetooth_exit(data); return err; } diff --git a/audio/ipc.h b/audio/ipc.h index 409abfda..d340d15e 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -44,6 +44,7 @@ #define PKT_TYPE_STATUS_RSP 3 #define PKT_TYPE_CTL_REQ 4 #define PKT_TYPE_CTL_RSP 5 +#define PKT_TYPE_CTL_NTFY 6 /* Errors codes */ #define PKT_ERROR_NONE 0 @@ -83,21 +84,26 @@ struct ipc_data_status { uint8_t status; /* Stream status */ } __attribute__ ((packed)); +#define CTL_MODE_PLAYBACK 0 +#define CTL_MODE_CAPTURE 1 +#define CTL_MODE_GENERAL 2 + /* Supported control operations */ -#define DATA_CTL_POWER 0x40 -#define DATA_CTL_VOL_UP 0x41 -#define DATA_CTL_VOL_DOWN 0x42 -#define DATA_CTL_MUTE 0x43 -#define DATA_CTL_PLAY 0x44 -#define DATA_CTL_STOP 0x45 -#define DATA_CTL_PAUSE 0x46 -#define DATA_CTL_RECORD 0x47 -#define DATA_CTL_REWIND 0x48 -#define DATA_CTL_FAST_FORWARD 0x49 -#define DATA_CTL_EJECT 0x4A -#define DATA_CTL_FORWARD 0x4B -#define DATA_CTL_BACKWARD 0x4C +#define CTL_KEY_POWER 0x40 +#define CTL_KEY_VOL_UP 0x41 +#define CTL_KEY_VOL_DOWN 0x42 +#define CTL_KEY_MUTE 0x43 +#define CTL_KEY_PLAY 0x44 +#define CTL_KEY_STOP 0x45 +#define CTL_KEY_PAUSE 0x46 +#define CTL_KEY_RECORD 0x47 +#define CTL_KEY_REWIND 0x48 +#define CTL_KEY_FAST_FORWARD 0x49 +#define CTL_KEY_EJECT 0x4A +#define CTL_KEY_FORWARD 0x4B +#define CTL_KEY_BACKWARD 0x4C struct ipc_data_ctl { - uint8_t operation; /* Operation ID */ + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ } __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d588bcfd..16c99f06 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -84,14 +84,27 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) return data->hw_ptr; } +static void bluetooth_exit(struct bluetooth_data *data) +{ + if (data == NULL) + return; + + if (data->sock >= 0) + close(data->sock); + + if (data->buffer) + free(data->buffer); + + free(data); +} + static int bluetooth_close(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; DBG("bluetooth_close %p", io); - free(data->buffer); - free(data); + bluetooth_exit(data); return 0; } @@ -517,6 +530,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); data = malloc(sizeof(struct bluetooth_data)); + memset(data, 0, sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; @@ -552,11 +566,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) return 0; error: - if (data) { - if (data->sock >= 0) - close(data->sock); - free(data); - } + bluetooth_exit(data); return err; } -- cgit From 7284382ac070c8643f59443bcdd253dd7384804b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 16 Jun 2007 14:34:34 +0000 Subject: Add Headset gain control methods --- audio/audio-api.txt | 18 ++++ audio/headset.c | 236 ++++++++++++++++++++++++++++++++++++++++++++-------- audio/manager.c | 6 ++ audio/manager.h | 1 + 4 files changed, 228 insertions(+), 33 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 355f4cfe..17baedc1 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -134,6 +134,24 @@ Methods void Connect() Returns true if an audio connection to the headset is active. + uint16 GetSpeakerGain() + + Returns the current speaker gain if available, + otherwise returns the error NotAvailable. + + uint16 GetMicrophoneGain() + + Returns the current microphone gain if available, + otherwise returns the error NotAvailable. + + void SetSpeakerGain(uint16 gain) + + Changes the current speaker gain if possible. + + void SetMicrophoneGain(uint16 gain) + + Changes the current speaker gain if possible. + Signals void AnswerRequested() Sent when the answer button is pressed on the headset diff --git a/audio/headset.c b/audio/headset.c index 6e5ec517..a5e7687c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -62,6 +62,9 @@ #define BUF_SIZE 1024 +#define HEADSET_GAIN_SPEAKER 'S' +#define HEADSET_GAIN_MICROPHONE 'M' + typedef enum { HEADSET_EVENT_KEYPRESS, HEADSET_EVENT_GAIN, @@ -107,6 +110,9 @@ struct headset { headset_state_t state; struct pending_connect *pending_connect; + + int sp_gain; + int mic_gain; }; static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, @@ -142,20 +148,31 @@ static void hs_signal_gain_setting(audio_device_t *device, const char *buf) return; } + gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); + + if (gain > 15) { + error("Invalid gain value received: %u", gain); + return; + } + switch (buf[3]) { - case 'S': + case HEADSET_GAIN_SPEAKER: + if (device->headset->sp_gain == gain) + return; name = "SpeakerGainChanged"; + device->headset->sp_gain = gain; break; - case 'M': + case HEADSET_GAIN_MICROPHONE: + if (device->headset->mic_gain == gain) + return; name = "MicrophoneGainChanged"; + device->headset->mic_gain = gain; break; default: error("Unknown gain setting"); return; } - gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); - dbus_connection_emit_signal(connection, device->object_path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, @@ -388,6 +405,32 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } +static GIOError headset_send(headset_t *hs, const char *str) +{ + GIOError err; + gsize total_written, written, count; + + assert(hs != NULL); + + if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { + error("headset_send: the headset is not connected"); + return G_IO_ERROR_UNKNOWN; + } + + count = strlen(str); + written = total_written = 0; + + while (total_written < count) { + err = g_io_channel_write(hs->rfcomm, str + total_written, + count - total_written, &written); + if (err != G_IO_ERROR_NONE) + return err; + total_written += written; + } + + return G_IO_ERROR_NONE; +} + static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { @@ -395,6 +438,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, int ret, sk, err, flags; socklen_t len; DBusMessage *reply; + char str[13]; if (cond & G_IO_NVAL) return FALSE; @@ -445,6 +489,16 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); + if (hs->sp_gain >= 0) { + snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", hs->sp_gain); + headset_send(device->headset, str); + } + + if (hs->mic_gain >= 0) { + snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", hs->sp_gain); + headset_send(device->headset, str); + } + return FALSE; failed: @@ -1266,40 +1320,13 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED;; } -static GIOError headset_send_ring(audio_device_t *device) -{ - struct headset *hs = device->headset; - const char *ring_str = "\r\nRING\r\n"; - GIOError err; - gsize total_written, written, count; - - assert(hs != NULL); - if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { - error("the headset %s is not connected", device->object_path); - return G_IO_ERROR_UNKNOWN; - } - - count = strlen(ring_str); - written = total_written = 0; - - while (total_written < count) { - err = g_io_channel_write(hs->rfcomm, ring_str + total_written, - count - total_written, &written); - if (err != G_IO_ERROR_NONE) - return err; - total_written += written; - } - - return G_IO_ERROR_NONE; -} - static gboolean ring_timer_cb(gpointer data) { audio_device_t *device = data; assert(device != NULL); - if (headset_send_ring(device) != G_IO_ERROR_NONE) + if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) error("Sending RING failed"); return TRUE; @@ -1328,7 +1355,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, goto done; } - if (headset_send_ring(device) != G_IO_ERROR_NONE) { + if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) { dbus_message_unref(reply); return err_failed(connection, msg, "Failed"); } @@ -1466,6 +1493,143 @@ failed: return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + audio_device_t *device = data; + struct headset *hs = device->headset; + DBusMessage *reply; + dbus_uint16_t gain; + + assert(hs); + + if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0) + return err_not_available(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + gain = (dbus_uint16_t) hs->sp_gain; + + dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + audio_device_t *device = data; + struct headset *hs = device->headset; + DBusMessage *reply; + dbus_uint16_t gain; + + assert(hs); + + if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0) + return err_not_available(conn, msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + gain = (dbus_uint16_t) hs->mic_gain; + + dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_set_gain(DBusConnection *conn, + DBusMessage *msg, + void *data, char type) +{ + audio_device_t *device = data; + struct headset *hs = device->headset; + DBusMessage *reply; + DBusError derr; + dbus_uint16_t gain; + char str[13]; + + assert(hs); + + if (hs->state < HEADSET_STATE_CONNECTED) + return err_not_connected(conn, msg); + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + err_invalid_args(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + if (gain > 15) + return err_invalid_args(conn, msg, + "Must be less than or equal to 15"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (hs->state != HEADSET_STATE_PLAYING) + goto done; + + snprintf(str, sizeof(str) - 1, "\r\n+VG%c=%u\r\n", type, gain); + + if (headset_send(device->headset, str) != G_IO_ERROR_NONE) { + dbus_message_unref(reply); + return err_failed(conn, msg, "Unable to send to headset"); + } + +done: + if (type == HEADSET_GAIN_SPEAKER) { + hs->sp_gain = gain; + dbus_connection_emit_signal(conn, device->object_path, + AUDIO_HEADSET_INTERFACE, + "SpeakerGainChanged", + DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + } + else { + hs->mic_gain = gain; + dbus_connection_emit_signal(conn, device->object_path, + AUDIO_HEADSET_INTERFACE, + "MicrophoneGainChanged", + DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + } + + send_message_and_unref(conn, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult hs_set_speaker_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return hs_set_gain(conn, msg, data, HEADSET_GAIN_SPEAKER); +} + +static DBusHandlerResult hs_set_mic_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + return hs_set_gain(conn, msg, data, HEADSET_GAIN_MICROPHONE); +} + static DBusMethodVTable headset_methods[] = { { "Connect", hs_connect, "", "" }, { "Disconnect", hs_disconnect, "", "" }, @@ -1475,6 +1639,10 @@ static DBusMethodVTable headset_methods[] = { { "Play", hs_play, "", "" }, { "Stop", hs_stop, "", "" }, { "IsPlaying", hs_is_playing, "", "b" }, + { "GetSpeakerGain", hs_get_speaker_gain, "", "q" }, + { "GetMicrophoneGain", hs_get_mic_gain, "", "q" }, + { "SetSpeakerGain", hs_set_speaker_gain, "q", "" }, + { "SetMicrophoneGain", hs_set_mic_gain, "q", "" }, { NULL, NULL, NULL, NULL } }; @@ -1560,6 +1728,8 @@ headset_t *headset_init(const char *object_path, sdp_record_t *record, headset = g_new0(headset_t, 1); headset->rfcomm_ch = -1; + headset->sp_gain = -1; + headset->mic_gain = -1; if (!record) goto register_iface; diff --git a/audio/manager.c b/audio/manager.c index c0b51976..e166fdca 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -143,6 +143,12 @@ DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) "Does not exist"); } +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, "org.bluez.audio.Error.NotAvailable", + "Not available"); +} + DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc) { diff --git a/audio/manager.h b/audio/manager.h index 1927a110..1ec5247b 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -91,6 +91,7 @@ DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_connect_failed(DBusConnection *conn, DBusMessage *msg, int err); DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc); -- cgit From 7318cfb762eab445b8181ddc279fe2e73c6c2e6e Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 18 Jun 2007 00:50:40 +0000 Subject: better messages to figure out why combo set fails --- audio/headset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a5e7687c..5c087312 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -917,7 +917,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) } if (!array) { - error("Unable to get handle array from reply"); + error("get_record_reply: Unable to get handle array from reply"); goto failed_not_supported; } @@ -1188,7 +1188,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) } if (!array) { - error("Unable to get handle array from reply"); + error("get_handles_reply: Unable to get handle array from reply"); if (c->msg) err_not_supported(connection, c->msg); goto failed; -- cgit From eb0899c1c7137ece8c0e5e5ec031cb2b9b4262e3 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 18 Jun 2007 21:08:35 +0000 Subject: Make deprecated headset methods to rely on new device methods. --- audio/audio-api.txt | 8 ++ audio/headset.c | 145 +++++++++++++----------- audio/headset.h | 4 +- audio/main.c | 2 - audio/manager.c | 321 +++++++++++++++++++++++++++------------------------- audio/manager.h | 2 +- audio/unix.c | 60 +++++----- audio/unix.h | 3 + 8 files changed, 289 insertions(+), 256 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 17baedc1..cd736802 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -35,6 +35,14 @@ Methods of available devices which implement as a minimum the interfaces given through the parameter. + string DefaultDevice() + + Returns the object path for the default device. + + void ChangeDefaultDevice(string path) + + Changes the default device. + array{string} ListHeadsets() Returns list of headset objects that are configured. diff --git a/audio/headset.c b/audio/headset.c index 5c087312..723bc1ab 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -89,6 +89,8 @@ struct pending_connect { DBusMessage *msg; GIOChannel *io; guint io_id; + int sock; + struct ipc_data_cfg *cfg; }; struct headset { @@ -325,7 +327,7 @@ static void send_cancel_auth(audio_device_t *device) DBusMessage *cancel; char addr[18], *address = addr; const char *uuid; - + if (device->headset->type == SVC_HEADSET) uuid = HSP_AG_UUID; else @@ -475,9 +477,11 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, g_io_add_watch(hs->sco, flags, (GIOFunc) sco_cb, device); - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(connection, reply); + if (hs->pending_connect->msg) { + reply = dbus_message_new_method_return(hs->pending_connect->msg); + if (reply) + send_message_and_unref(connection, reply); + } pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -514,6 +518,70 @@ failed: return FALSE; } +static int sco_connect(audio_device_t *device, struct pending_connect *c) +{ + struct headset *hs = device->headset; + struct sockaddr_sco addr; + gboolean do_callback = FALSE; + int sk, err; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + return -err; + } + + c->io = g_io_channel_unix_new(sk); + if (!c->io) { + close(sk); + return -EINVAL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, BDADDR_ANY); + + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + return -err; + } + + if (set_nonblocking(sk) < 0) { + err = errno; + return -err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, &device->bda); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect: %s (%d)", strerror(errno), errno); + return -err; + } + + debug("SCO connect in progress"); + c->io_id = g_io_add_watch(c->io, + G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + (GIOFunc) sco_connect_cb, device); + } else { + debug("SCO connect succeeded with first try"); + do_callback = TRUE; + } + + hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; + hs->pending_connect = c; + + if (do_callback) + sco_connect_cb(c->io, G_IO_OUT, device); + + return 0; +} + static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, audio_device_t *device) { @@ -521,14 +589,14 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, char hs_address[18]; int sk, ret, err; socklen_t len; - + if (cond & G_IO_NVAL) return FALSE; hs = device->headset; assert(hs != NULL); - assert(hs->pending_connect != NULL); + assert(hs->pending_connect != NULL); assert(hs->rfcomm == NULL); assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); @@ -1406,10 +1474,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, { audio_device_t *device = data; struct headset *hs = device->headset; - struct sockaddr_sco addr; struct pending_connect *c; - gboolean do_callback = FALSE; - int sk, err; if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(connection, msg); @@ -1426,66 +1491,10 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, c->msg = msg ? dbus_message_ref(msg) : NULL; - sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); - if (sk < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(connection, msg, err); - goto failed; - } - - c->io = g_io_channel_unix_new(sk); - if (!c->io) { - close(sk); - pending_connect_free(c); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, BDADDR_ANY); - - if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - err_connect_failed(connection, msg, err); + if (sco_connect(device, c) < 0) goto failed; - } - - if (set_nonblocking(sk) < 0) { - err = errno; - err_connect_failed(connection, msg, err); - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &device->bda); - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - if (!(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect: %s (%d)", strerror(errno), errno); - goto failed; - } - - debug("SCO connect in progress"); - c->io_id = g_io_add_watch(c->io, - G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, device); - } else { - debug("SCO connect succeeded with first try"); - do_callback = TRUE; - } - - hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - hs->pending_connect = c; - - if (do_callback) - sco_connect_cb(c->io, G_IO_OUT, device); return 0; - failed: if (c) pending_connect_free(c); @@ -1772,7 +1781,7 @@ register_iface: } void headset_free(const char *object_path) -{ +{ audio_device_t *device; if (!dbus_connection_get_object_user_data(connection, object_path, @@ -2028,7 +2037,7 @@ void headset_exit(void) connection = NULL; } -int headset_get_config(headset_t *headset, struct ipc_data_cfg *cfg) +int headset_get_config(headset_t *headset, int sock, struct ipc_data_cfg *cfg) { if (!sco_over_hci) return -1; diff --git a/audio/headset.h b/audio/headset.h index 6780e7a9..b420860e 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -29,7 +29,7 @@ #include -#include "ipc.h" +#include "unix.h" #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" @@ -49,6 +49,6 @@ int headset_server_init(DBusConnection *conn, gboolean disable_hfp, void headset_exit(void); -int headset_get_config(headset_t *headset, struct ipc_data_cfg *cfg); +int headset_get_config(headset_t *headset, int sock, struct ipc_data_cfg *cfg); #endif /* __AUDIO_HEADSET_H_ */ diff --git a/audio/main.c b/audio/main.c index 3a170b1a..0ca8ce87 100644 --- a/audio/main.c +++ b/audio/main.c @@ -36,9 +36,7 @@ #include "dbus.h" #include "logging.h" -#include "unix.h" #include "manager.h" -#include "headset.h" /* Configuration settings */ static gboolean disable_hfp = TRUE; diff --git a/audio/manager.c b/audio/manager.c index e166fdca..7faa0490 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -79,7 +79,7 @@ struct audio_sdp_data { static DBusConnection *connection = NULL; -static audio_device_t *default_hs = NULL; +static audio_device_t *default_dev = NULL; static GSList *devices = NULL; @@ -275,6 +275,10 @@ static gboolean add_device(audio_device_t *device) return FALSE; } + /* First device became default */ + if (g_slist_length(devices) == 0) + default_dev = device; + devices = g_slist_append(devices, device); return TRUE; @@ -370,6 +374,11 @@ static void handle_record(sdp_record_t *record, audio_device_t *device) break; case HANDSFREE_SVCLASS_ID: debug("Found Hansfree record"); + if (device->headset) + headset_update(device->headset, record, uuid16); + else + device->headset = headset_init(device->object_path, + record, uuid16); break; case HANDSFREE_AGW_SVCLASS_ID: debug("Found Handsfree AG record"); @@ -429,6 +438,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) char **required; int required_len, i; DBusMessage *reply = NULL; + DBusError derr; debug("Audio service discovery completed with %s", success ? "success" : "failure"); @@ -441,13 +451,43 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (!data->msg) goto update; - if (!dbus_message_get_args(data->msg, NULL, + dbus_error_init(&derr); + if (dbus_message_is_method_call(data->msg, AUDIO_MANAGER_INTERFACE, + "CreateHeadset")) { + dbus_message_get_args(data->msg, &derr, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID); + required = dbus_new0(char *, 2); + if (required == NULL) { + success = FALSE; + err_failed(connection, data->msg, "Out of memory"); + goto done; + } + + required[0] = dbus_new0(char, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + if (required[0] == NULL) { + success = FALSE; + err_failed(connection, data->msg, "Out of memory"); + goto done; + } + + memcpy(required[0], AUDIO_HEADSET_INTERFACE, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + required[1] = NULL; + required_len = 1; + } + else + dbus_message_get_args(data->msg, &derr, DBUS_TYPE_STRING, &addr, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &required, &required_len, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { error("Unable to get message args"); success = FALSE; + dbus_error_free(&derr); goto done; } @@ -494,13 +534,20 @@ update: "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - + if (data->device->headset) + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); send_message_and_unref(connection, reply); } done: + if (required) + dbus_free_string_array(required); if (!success) free_device(data->device); if (data->msg) @@ -651,7 +698,6 @@ static void get_handles_reply(DBusPendingCall *call, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { - err_failed(connection, data->msg, "Unable to get args from reply"); goto failed; @@ -679,7 +725,7 @@ static void get_handles_reply(DBusPendingCall *call, get_next_record(data); else finish_sdp(data, TRUE); - } + } dbus_message_unref(reply); @@ -789,8 +835,8 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - if (!default_hs) { - default_hs = device; + if (!default_dev) { + default_dev = device; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", @@ -805,13 +851,13 @@ static gboolean device_supports_interface(audio_device_t *device, const char *iface) { if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) - return device->headset ? TRUE : FALSE; + return device->headset ? TRUE : FALSE; if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) return device->gateway ? TRUE : FALSE; if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) - return device->gateway ? TRUE : FALSE; + return device->source ? TRUE : FALSE; if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) return device->sink ? TRUE : FALSE; @@ -854,11 +900,34 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBusError derr; dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &required, &required_len, - DBUS_TYPE_INVALID); + if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, + "CreateHeadset")) { + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + required = dbus_new0(char *, 2); + if (required == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + required[0] = dbus_new0(char, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + if (required[0] == NULL) { + dbus_free(required); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + memcpy(required[0], AUDIO_HEADSET_INTERFACE, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + required[1] = NULL; + required_len = 1; + } + else + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); @@ -905,10 +974,31 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, int required_len; dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &required, &required_len, - DBUS_TYPE_INVALID); + + if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, + "ListHeadsets")) { + required = dbus_new0(char *, 2); + if (required == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + required[0] = dbus_new0(char, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + if (required[0] == NULL) { + dbus_free(required); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + memcpy(required[0], AUDIO_HEADSET_INTERFACE, + strlen(AUDIO_HEADSET_INTERFACE) + 1); + required[1] = NULL; + required_len = 1; + } + else + dbus_message_get_args(msg, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, + &required, &required_len, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); dbus_error_free(&derr); @@ -944,72 +1034,6 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_create_headset(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - const char *path, *address; - bdaddr_t bda; - DBusMessage *reply; - DBusError derr; - audio_device_t *device; - gboolean created = FALSE; - - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); - if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } - - str2ba(address, &bda); - - device = find_device(&bda); - if (device) - goto done; - - device = create_device(&bda); - if (!add_device(device)) { - free_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to create new audio device"); - } - - if (!device->headset) { - device->headset = headset_init(device->object_path, NULL, 0); - created = TRUE; - } - - if (!device->headset) { - remove_device(device); - return error_reply(connection, msg, - "org.bluez.audio.Error.Failed", - "Unable to init Headset interface"); - } - -done: - path = device->object_path; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - if (created) - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "HeadsetCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - return send_message_and_unref(connection, reply); -} - static gint device_path_cmp(gconstpointer a, gconstpointer b) { const audio_device_t *device = a; @@ -1045,7 +1069,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", - "The headset does not exist"); + "The device does not exist"); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1055,20 +1079,20 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, remove_device(device); - if (default_hs == device) { + if (default_dev == device) { const char *param; GSList *l; - default_hs = NULL; + default_dev = NULL; for (l = devices; l != NULL; l = l->next) { device = l->data; if (device->headset) - default_hs = device; + default_dev = device; } - param = default_hs ? default_hs->object_path : ""; + param = default_dev ? default_dev->object_path : ""; dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -1092,49 +1116,6 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_remove_headset(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - return am_remove_device(conn, msg, data); -} - -static DBusHandlerResult am_list_headsets(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - DBusMessageIter iter; - DBusMessageIter array_iter; - DBusMessage *reply; - GSList *l; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array_iter); - - for (l = devices; l != NULL; l = l->next) { - audio_device_t *device = l->data; - const char *path; - - if (!device->headset) - continue; - - path = device->object_path; - - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &path); - } - - dbus_message_iter_close_container(&iter, &array_iter); - - return send_message_and_unref(connection, reply); -} - static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBusMessage *msg, void *data) @@ -1174,14 +1155,21 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, return send_message_and_unref(conn, reply); } -static DBusHandlerResult am_get_default_headset(DBusConnection *conn, +static DBusHandlerResult am_default_device(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessage *reply; const char *path; - if (!default_hs) + if (!default_dev) + return error_reply(connection, msg, + "org.bluez.audio.Error.DoesNotExist", + "There is no default device"); + + if (default_dev->headset == NULL && + dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, + "DefaultHeadset")) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", "There is no default headset"); @@ -1190,7 +1178,7 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - path = default_hs->object_path; + path = default_dev->object_path; dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); @@ -1198,7 +1186,7 @@ static DBusHandlerResult am_get_default_headset(DBusConnection *conn, return send_message_and_unref(connection, reply); } -static DBusHandlerResult am_change_default_headset(DBusConnection *conn, +static DBusHandlerResult am_change_default_device(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -1206,6 +1194,7 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, DBusMessage *reply; GSList *match; const char *path; + audio_device_t *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -1224,22 +1213,35 @@ static DBusHandlerResult am_change_default_headset(DBusConnection *conn, if (!match) return error_reply(connection, msg, "org.bluez.audio.Error.DoesNotExist", - "The headset does not exist"); + "The device does not exist"); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - default_hs = match->data; + device = match->data; - path = default_hs->object_path; + path = device->object_path; - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (!dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, + "ChangeDefaultHeadset")) + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultDeviceChanged", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + else if (device->headset) + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + else + return error_reply(connection, msg, + "org.bluez.audio.Error.DoesNotExist", + "The headset does not exist"); + default_dev = device; return send_message_and_unref(connection, reply); } @@ -1249,19 +1251,23 @@ static DBusMethodVTable manager_methods[] = { { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, - "as", "as" }, - { "CreateHeadset", am_create_headset, + "as", "as" }, + { "DefaultDevice", am_default_device, + "", "s" }, + { "ChangeDefaultDevice", am_change_default_device, + "s", "" }, + { "CreateHeadset", am_create_device, "s", "s" }, - { "RemoveHeadset", am_remove_headset, + { "RemoveHeadset", am_remove_device, "s", "" }, - { "ListHeadsets", am_list_headsets, + { "ListHeadsets", am_list_devices, "", "as" }, { "FindDeviceByAddress", am_find_by_addr, "s", "s" }, - { "DefaultHeadset", am_get_default_headset, + { "DefaultHeadset", am_default_device, "", "s" }, - { "ChangeDefaultHeadset", am_change_default_headset, - "s", "" }, + { "ChangeDefaultHeadset", am_change_default_device, + "s", "" }, { NULL, NULL, NULL, NULL }, }; @@ -1270,6 +1276,7 @@ static DBusSignalVTable manager_signals[] = { { "DeviceRemoved", "s" }, { "HeadsetCreated", "s" }, { "HeadsetRemoved", "s" }, + { "DefaultDeviceChanged", "s" }, { "DefaultHeadsetChanged", "s" }, { NULL, NULL } }; @@ -1309,19 +1316,19 @@ void audio_exit(void) connection = NULL; } -int manager_get_device(uint8_t role, struct ipc_data_cfg *cfg) +int manager_get_device(int sock, uint8_t role, struct ipc_data_cfg *cfg) { GSList *l; - if (default_hs && default_hs->headset && - headset_is_connected(default_hs->headset)) - return headset_get_config(default_hs->headset, cfg); + if (default_dev && default_dev->headset && + headset_is_connected(default_dev->headset)) + return headset_get_config(default_dev->headset, sock, cfg); for (l = devices; l != NULL; l = l->next) { audio_device_t *dev = l->data; if (dev->headset && headset_is_connected(dev->headset)) - return headset_get_config(dev->headset, cfg); + return headset_get_config(dev->headset, sock, cfg); } return -1; diff --git a/audio/manager.h b/audio/manager.h index 1ec5247b..5cdf2b23 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -95,4 +95,4 @@ DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, const char *dsc); -int manager_get_device(uint8_t role, struct ipc_data_cfg *cfg); +int manager_get_device(int sock, uint8_t role, struct ipc_data_cfg *cfg); diff --git a/audio/unix.c b/audio/unix.c index 163f4f98..2dad00df 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -39,7 +39,6 @@ #include "logging.h" #include "dbus.h" -#include "unix.h" #include "manager.h" static int unix_sock = -1; @@ -119,29 +118,8 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) cfg = (struct ipc_data_cfg *) pkt->data; memset(cfg, 0, sizeof(struct ipc_data_cfg)); - if (manager_get_device(pkt->role, cfg) < 0) - cfg->fd = -1; - - info("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," - "rate=%u", cfg->fd, cfg->fd_opt, cfg->channels, - cfg->pkt_len, cfg->sample_size, cfg->rate); - - pkt->type = PKT_TYPE_CFG_RSP; - pkt->length = sizeof(struct ipc_data_cfg); - pkt->error = PKT_ERROR_NONE; - - len = send(clisk, pkt, len, 0); - if (len < 0) - info("Error %s(%d)", strerror(errno), errno); - - info("%d bytes sent", len); - - if (cfg->fd != -1) { - len = unix_sendmsg_fd(clisk, cfg->fd, pkt); - if (len < 0) - info("Error %s(%d)", strerror(errno), errno); - info("%d bytes sent", len); - } + if (manager_get_device(clisk, pkt->role, cfg) == 0) + unix_send_cfg(clisk, pkt); break; case PKT_TYPE_STATUS_REQ: @@ -152,8 +130,6 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) break; } - g_free(pkt); - close(clisk); return TRUE; } @@ -202,3 +178,35 @@ void unix_exit(void) close(unix_sock); unix_sock = -1; } + +int unix_send_cfg(int sock, struct ipc_packet *pkt) +{ + struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; + int len; + + info("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," + "rate=%u", cfg->fd, cfg->fd_opt, cfg->channels, + cfg->pkt_len, cfg->sample_size, cfg->rate); + + pkt->type = PKT_TYPE_CFG_RSP; + pkt->length = sizeof(struct ipc_data_cfg); + pkt->error = PKT_ERROR_NONE; + + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + len = send(sock, pkt, len, 0); + if (len < 0) + info("Error %s(%d)", strerror(errno), errno); + + info("%d bytes sent", len); + + if (cfg->fd != -1) { + len = unix_sendmsg_fd(sock, cfg->fd, pkt); + if (len < 0) + info("Error %s(%d)", strerror(errno), errno); + info("%d bytes sent", len); + } + + g_free(pkt); + close(sock); + return 0; +} diff --git a/audio/unix.h b/audio/unix.h index 4f3fc994..15da23ab 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -21,6 +21,9 @@ * */ +#include "ipc.h" + int unix_init(void); void unix_exit(void); +int unix_send_cfg(int sock, struct ipc_packet *pkt); -- cgit From 1ad4df62d7b0ca988bc5ae62444420fc0273d53a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 22 Jun 2007 19:47:53 +0000 Subject: Merge changes from git://git.infradead.org/users/vudentz/bluez-utils.git. --- audio/device.c | 201 ++++++++++++++++++++ audio/device.h | 73 ++++++++ audio/error.c | 99 ++++++++++ audio/error.h | 36 ++++ audio/gateway.c | 555 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/gateway.h | 30 +++ 6 files changed, 994 insertions(+) create mode 100644 audio/device.c create mode 100644 audio/device.h create mode 100644 audio/error.c create mode 100644 audio/error.h create mode 100644 audio/gateway.c create mode 100644 audio/gateway.h (limited to 'audio') diff --git a/audio/device.c b/audio/device.c new file mode 100644 index 00000000..50d07a2c --- /dev/null +++ b/audio/device.c @@ -0,0 +1,201 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "dbus.h" +#include "dbus-helper.h" +#include "logging.h" + +#include "device.h" + +void device_finish_sdp_transaction(struct device *device) +{ + char address[18], *addr_ptr = address; + DBusMessage *msg, *reply; + DBusError derr; + + ba2str(&device->bda, address); + + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", + "FinishRemoteServiceTransaction"); + if (!msg) { + error("Unable to allocate new method call"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(device->conn, msg, -1, + &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("FinishRemoteServiceTransaction(%s) failed: %s", + address, derr.message); + dbus_error_free(&derr); + return; + } + + dbus_message_unref(reply); +} + +static DBusHandlerResult device_get_address(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + DBusMessage *reply; + char address[18], *ptr = address; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + ba2str(&device->bda, address); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + +static DBusHandlerResult device_get_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + DBusMessageIter iter, array_iter; + struct device *device = data; + DBusMessage *reply; + const char *iface; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + if (device->headset && + headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(conn, reply); +} + +static DBusMethodVTable device_methods[] = { + { "GetAddress", device_get_address, + "", "s" }, + { "GetConnectedInterfaces", device_get_connected, + "", "s" }, + { NULL, NULL, NULL, NULL } +}; + +static void device_free(struct device *device) +{ + if (device->headset) + headset_free(device); + + if (device->conn) + dbus_connection_unref(device->conn); + + if (device->adapter_path) + g_free(device->adapter_path); + + if (device->path) + g_free(device->path); + + g_free(device); +} + +static void device_unregister(DBusConnection *conn, void *data) +{ + struct device *device = data; + + info("Unregistered device path:%s", device->path); + + device_free(device); +} + +struct device * device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) +{ + struct device *device; + bdaddr_t src; + int dev_id; + + if (!conn || !path) + return NULL; + + bacpy(&src, BDADDR_ANY); + dev_id = hci_get_route(NULL); + if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) + return NULL; + + device = g_new0(struct device, 1); + + if (!dbus_connection_create_object_path(conn, path, device, + device_unregister)) { + error("D-Bus failed to register %s path", path); + device_free(device); + return NULL; + } + + if (!dbus_connection_register_interface(conn, + path, + AUDIO_DEVICE_INTERFACE, + device_methods, NULL, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_DEVICE_INTERFACE, path); + dbus_connection_destroy_object_path(conn, path); + return NULL; + } + + device->path = g_strdup(path); + bacpy(&device->bda, bda); + device->conn = dbus_connection_ref(conn); + device->adapter_path = g_malloc0(16); + snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); + + return device; +} diff --git a/audio/device.h b/audio/device.h new file mode 100644 index 00000000..daa9d6d1 --- /dev/null +++ b/audio/device.h @@ -0,0 +1,73 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#include + +#include "headset.h" +#include "gateway.h" + +#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" + +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" + +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" + +#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" +#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" + +#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" + +#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" +#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" + +#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" +#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" + +/* Move these to respective .h files once they exist */ +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" +#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" +#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" +struct sink; +struct source; +struct control; +struct target; + +struct device { + DBusConnection *conn; + char *adapter_path; + char *path; + bdaddr_t bda; + + struct headset *headset; + struct gateway *gateway; + struct sink *sink; + struct source *source; + struct control *control; + struct target *target; +}; + +struct device *device_register(DBusConnection *conn, const char *path, + bdaddr_t *bda); +void device_finish_sdp_transaction(struct device *device); diff --git a/audio/error.c b/audio/error.c new file mode 100644 index 00000000..97307030 --- /dev/null +++ b/audio/error.c @@ -0,0 +1,99 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "error.h" +#include "logging.h" + +#define AUDIO_ERROR_INTERFACE "org.bluez.audio.Error" + +/* FIXME: Remove these once global error functions exist */ +static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr) +{ + DBusMessage *derr; + + if (!conn || !msg) + return DBUS_HANDLER_RESULT_HANDLED; + + derr = dbus_message_new_error(msg, name, descr); + if (!derr) { + error("Unable to allocate new error return"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + return send_message_and_unref(conn, derr); +} + +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".InvalidArguments", + descr ? descr : "Invalid arguments in method call"); +} + +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".AlreadyConnected", + "Already connected to a device"); +} + +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotConnected", + "Not connected to any device"); +} + +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotSupported", + "The service is not supported by the remote device"); +} + +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, const char *err) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".ConnectFailed", + err); +} + +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".DoesNotExist", + "Does not exist"); +} + +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, ".NotAvailable", + "Not available"); +} + +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); +} diff --git a/audio/error.h b/audio/error.h new file mode 100644 index 00000000..833943af --- /dev/null +++ b/audio/error.h @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#include "dbus.h" + +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr); +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, const char *err); +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc); diff --git a/audio/gateway.c b/audio/gateway.c new file mode 100644 index 00000000..687d0969 --- /dev/null +++ b/audio/gateway.c @@ -0,0 +1,555 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + +#include "dbus.h" +#include "dbus-helper.h" +#include "logging.h" +#include "manager.h" +#include "error.h" + +static gboolean disable_hfp = FALSE; +static gboolean sco_over_hci = TRUE; + +static uint32_t hs_record_id = 0; +static uint32_t hf_record_id = 0; + +static GIOChannel *hs_server = NULL; +static GIOChannel *hf_server = NULL; + +static DBusConnection *connection = NULL; + +static int gateway_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *channel; + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 gateway_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0105; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 uint32_t gateway_add_ag_record(uint8_t channel, sdp_buf_t *buf) +{ + DBusMessage *msg, *reply; + DBusError derr; + dbus_uint32_t rec_id; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", "AddServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &buf->data, buf->data_size, DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("Adding service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Invalid arguments to AddServiceRecord reply: %s", + derr.message); + dbus_message_unref(reply); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + debug("add_ag_record: got record id 0x%x", rec_id); + + return rec_id; +} + +static int gateway_remove_ag_record(uint32_t rec_id) +{ + DBusMessage *msg, *reply; + DBusError derr; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RemoveServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr)) { + error("Removing service record 0x%x failed: %s", + rec_id, derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + return 0; +} + +static void send_cancel_auth(struct device *device) +{ + DBusMessage *cancel; + char addr[18], *address = addr; + const char *uuid; + + if (headset_get_type(device) == SVC_HEADSET) + uuid = HSP_AG_UUID; + else + uuid = HFP_AG_UUID; + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(&device->bda, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); +} + +static void auth_cb(DBusPendingCall *call, void *data) +{ + struct device *device = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + 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"); + send_cancel_auth(device); + } + dbus_error_free(&err); + headset_close_rfcomm(device); + } else { + char hs_address[18]; + + headset_set_state(device, HEADSET_STATE_CONNECTED); + + ba2str(&device->bda, hs_address); + + debug("Accepted headset connection from %s for %s", hs_address, + device->path); + } + + dbus_message_unref(reply); +} + +static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, + void *data) +{ + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + char hs_address[18], *address = hs_address; + const char *uuid; + struct device *device; + DBusMessage *auth; + DBusPendingCall *pending; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + device = manager_device_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; + } + + if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + if (headset_connect_rfcomm(device, cli_sk) < 0) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + return TRUE; + } + + if (chan == hs_server) { + headset_set_type(device, SVC_HEADSET); + uuid = HSP_AG_UUID; + } else { + headset_set_type(device, SVC_HANDSFREE); + uuid = HFP_AG_UUID; + } + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocate RequestAuthorization method call"); + goto failed; + } + + ba2str(&device->bda, hs_address); + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { + error("Sending of authorization request failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, auth_cb, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(auth); + + return TRUE; + +failed: + headset_close_rfcomm(device); + + return TRUE; +} + +static GIOChannel *server_socket(uint8_t *channel) +{ + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = channel ? *channel : 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) +{ + uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; + + connection = dbus_connection_ref(conn); + + hs_server = server_socket(&chan); + if (!hs_server) + return -1; + + if (gateway_hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hs_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hs_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hs_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); + + disable_hfp = no_hfp; + + sco_over_hci = sco_hci; + + if (disable_hfp) + return 0; + + chan = DEFAULT_HF_AG_CHANNEL; + + hf_server = server_socket(&chan); + if (!hf_server) + return -1; + + if (gateway_hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hf_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); + + return 0; +} + +void gateway_exit(void) +{ + if (hs_record_id) { + gateway_remove_ag_record(hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + + if (hf_record_id) { + gateway_remove_ag_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } + + dbus_connection_unref(connection); + connection = NULL; +} diff --git a/audio/gateway.h b/audio/gateway.h new file mode 100644 index 00000000..137187cb --- /dev/null +++ b/audio/gateway.h @@ -0,0 +1,30 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" + +struct gateway; + +int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); + +void gateway_exit(void); -- cgit From d42edde61115b525ac1e9fe6ba8ead1c791d12ca Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 22 Jun 2007 20:09:09 +0000 Subject: Remove wrongly committed files --- audio/device.c | 201 -------------------- audio/device.h | 73 -------- audio/error.c | 99 ---------- audio/error.h | 36 ---- audio/gateway.c | 555 -------------------------------------------------------- audio/gateway.h | 30 --- 6 files changed, 994 deletions(-) delete mode 100644 audio/device.c delete mode 100644 audio/device.h delete mode 100644 audio/error.c delete mode 100644 audio/error.h delete mode 100644 audio/gateway.c delete mode 100644 audio/gateway.h (limited to 'audio') diff --git a/audio/device.c b/audio/device.c deleted file mode 100644 index 50d07a2c..00000000 --- a/audio/device.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 "dbus.h" -#include "dbus-helper.h" -#include "logging.h" - -#include "device.h" - -void device_finish_sdp_transaction(struct device *device) -{ - char address[18], *addr_ptr = address; - DBusMessage *msg, *reply; - DBusError derr; - - ba2str(&device->bda, address); - - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, - "org.bluez.Adapter", - "FinishRemoteServiceTransaction"); - if (!msg) { - error("Unable to allocate new method call"); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(device->conn, msg, -1, - &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("FinishRemoteServiceTransaction(%s) failed: %s", - address, derr.message); - dbus_error_free(&derr); - return; - } - - dbus_message_unref(reply); -} - -static DBusHandlerResult device_get_address(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct device *device = data; - DBusMessage *reply; - char address[18], *ptr = address; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - ba2str(&device->bda, address); - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_INVALID); - - return send_message_and_unref(conn, reply); -} - -static DBusHandlerResult device_get_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - DBusMessageIter iter, array_iter; - struct device *device = data; - DBusMessage *reply; - const char *iface; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array_iter); - - if (device->headset && - headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { - iface = AUDIO_HEADSET_INTERFACE; - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &iface); - } - - dbus_message_iter_close_container(&iter, &array_iter); - - return send_message_and_unref(conn, reply); -} - -static DBusMethodVTable device_methods[] = { - { "GetAddress", device_get_address, - "", "s" }, - { "GetConnectedInterfaces", device_get_connected, - "", "s" }, - { NULL, NULL, NULL, NULL } -}; - -static void device_free(struct device *device) -{ - if (device->headset) - headset_free(device); - - if (device->conn) - dbus_connection_unref(device->conn); - - if (device->adapter_path) - g_free(device->adapter_path); - - if (device->path) - g_free(device->path); - - g_free(device); -} - -static void device_unregister(DBusConnection *conn, void *data) -{ - struct device *device = data; - - info("Unregistered device path:%s", device->path); - - device_free(device); -} - -struct device * device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) -{ - struct device *device; - bdaddr_t src; - int dev_id; - - if (!conn || !path) - return NULL; - - bacpy(&src, BDADDR_ANY); - dev_id = hci_get_route(NULL); - if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) - return NULL; - - device = g_new0(struct device, 1); - - if (!dbus_connection_create_object_path(conn, path, device, - device_unregister)) { - error("D-Bus failed to register %s path", path); - device_free(device); - return NULL; - } - - if (!dbus_connection_register_interface(conn, - path, - AUDIO_DEVICE_INTERFACE, - device_methods, NULL, NULL)) { - error("Failed to register %s interface to %s", - AUDIO_DEVICE_INTERFACE, path); - dbus_connection_destroy_object_path(conn, path); - return NULL; - } - - device->path = g_strdup(path); - bacpy(&device->bda, bda); - device->conn = dbus_connection_ref(conn); - device->adapter_path = g_malloc0(16); - snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); - - return device; -} diff --git a/audio/device.h b/audio/device.h deleted file mode 100644 index daa9d6d1..00000000 --- a/audio/device.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 - * - */ - -#include - -#include "headset.h" -#include "gateway.h" - -#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" - -#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" - -#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" -#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" - -#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" -#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" - -#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" - -#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" -#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" - -#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" -#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" - -/* Move these to respective .h files once they exist */ -#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" -#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" -#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" -#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" -struct sink; -struct source; -struct control; -struct target; - -struct device { - DBusConnection *conn; - char *adapter_path; - char *path; - bdaddr_t bda; - - struct headset *headset; - struct gateway *gateway; - struct sink *sink; - struct source *source; - struct control *control; - struct target *target; -}; - -struct device *device_register(DBusConnection *conn, const char *path, - bdaddr_t *bda); -void device_finish_sdp_transaction(struct device *device); diff --git a/audio/error.c b/audio/error.c deleted file mode 100644 index 97307030..00000000 --- a/audio/error.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 "error.h" -#include "logging.h" - -#define AUDIO_ERROR_INTERFACE "org.bluez.audio.Error" - -/* FIXME: Remove these once global error functions exist */ -static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *descr) -{ - DBusMessage *derr; - - if (!conn || !msg) - return DBUS_HANDLER_RESULT_HANDLED; - - derr = dbus_message_new_error(msg, name, descr); - if (!derr) { - error("Unable to allocate new error return"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - return send_message_and_unref(conn, derr); -} - -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".InvalidArguments", - descr ? descr : "Invalid arguments in method call"); -} - -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".AlreadyConnected", - "Already connected to a device"); -} - -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotConnected", - "Not connected to any device"); -} - -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotSupported", - "The service is not supported by the remote device"); -} - -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".ConnectFailed", - err); -} - -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".DoesNotExist", - "Does not exist"); -} - -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, ".NotAvailable", - "Not available"); -} - -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); -} diff --git a/audio/error.h b/audio/error.h deleted file mode 100644 index 833943af..00000000 --- a/audio/error.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 - * - */ - -#include "dbus.h" - -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr); -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err); -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc); diff --git a/audio/gateway.c b/audio/gateway.c deleted file mode 100644 index 687d0969..00000000 --- a/audio/gateway.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 - -#include "dbus.h" -#include "dbus-helper.h" -#include "logging.h" -#include "manager.h" -#include "error.h" - -static gboolean disable_hfp = FALSE; -static gboolean sco_over_hci = TRUE; - -static uint32_t hs_record_id = 0; -static uint32_t hf_record_id = 0; - -static GIOChannel *hs_server = NULL; -static GIOChannel *hf_server = NULL; - -static DBusConnection *connection = NULL; - -static int gateway_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - sdp_data_t *channel; - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); - profile.version = 0x0100; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 gateway_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - uint16_t u16 = 0x0009; - sdp_data_t *channel, *features; - uint8_t netid = 0x01; - sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - features = sdp_data_alloc(SDP_UINT16, &u16); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); - - sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 uint32_t gateway_add_ag_record(uint8_t channel, sdp_buf_t *buf) -{ - DBusMessage *msg, *reply; - DBusError derr; - dbus_uint32_t rec_id; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "AddServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf->data, buf->data_size, DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("Adding service record failed: %s", derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", - derr.message); - dbus_message_unref(reply); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - debug("add_ag_record: got record id 0x%x", rec_id); - - return rec_id; -} - -static int gateway_remove_ag_record(uint32_t rec_id) -{ - DBusMessage *msg, *reply; - DBusError derr; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RemoveServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", - rec_id, derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - return 0; -} - -static void send_cancel_auth(struct device *device) -{ - DBusMessage *cancel; - char addr[18], *address = addr; - const char *uuid; - - if (headset_get_type(device) == SVC_HEADSET) - uuid = HSP_AG_UUID; - else - uuid = HFP_AG_UUID; - - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(&device->bda, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - send_message_and_unref(connection, cancel); -} - -static void auth_cb(DBusPendingCall *call, void *data) -{ - struct device *device = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - 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"); - send_cancel_auth(device); - } - dbus_error_free(&err); - headset_close_rfcomm(device); - } else { - char hs_address[18]; - - headset_set_state(device, HEADSET_STATE_CONNECTED); - - ba2str(&device->bda, hs_address); - - debug("Accepted headset connection from %s for %s", hs_address, - device->path); - } - - dbus_message_unref(reply); -} - -static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, - void *data) -{ - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; - char hs_address[18], *address = hs_address; - const char *uuid; - struct device *device; - DBusMessage *auth; - DBusPendingCall *pending; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - device = manager_device_connected(&addr.rc_bdaddr); - if (!device) { - close(cli_sk); - return TRUE; - } - - if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { - debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; - } - - if (headset_connect_rfcomm(device, cli_sk) < 0) { - error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; - } - - if (chan == hs_server) { - headset_set_type(device, SVC_HEADSET); - uuid = HSP_AG_UUID; - } else { - headset_set_type(device, SVC_HANDSFREE); - uuid = HFP_AG_UUID; - } - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); - goto failed; - } - - ba2str(&device->bda, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { - error("Sending of authorization request failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, auth_cb, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); - - return TRUE; - -failed: - headset_close_rfcomm(device); - - return TRUE; -} - -static GIOChannel *server_socket(uint8_t *channel) -{ - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = channel ? *channel : 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; -} - -int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) -{ - uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_buf_t buf; - - connection = dbus_connection_ref(conn); - - hs_server = server_socket(&chan); - if (!hs_server) - return -1; - - if (gateway_hsp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - hs_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hs_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hs_server); - hs_server = NULL; - return -1; - } - - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); - - disable_hfp = no_hfp; - - sco_over_hci = sco_hci; - - if (disable_hfp) - return 0; - - chan = DEFAULT_HF_AG_CHANNEL; - - hf_server = server_socket(&chan); - if (!hf_server) - return -1; - - if (gateway_hfp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - hf_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hf_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hf_server); - hs_server = NULL; - return -1; - } - - g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); - - return 0; -} - -void gateway_exit(void) -{ - if (hs_record_id) { - gateway_remove_ag_record(hs_record_id); - hs_record_id = 0; - } - - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; - } - - if (hf_record_id) { - gateway_remove_ag_record(hf_record_id); - hf_record_id = 0; - } - - if (hf_server) { - g_io_channel_unref(hf_server); - hf_server = NULL; - } - - dbus_connection_unref(connection); - connection = NULL; -} diff --git a/audio/gateway.h b/audio/gateway.h deleted file mode 100644 index 137187cb..00000000 --- a/audio/gateway.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 - * - */ - -#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" - -struct gateway; - -int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); - -void gateway_exit(void); -- cgit From a53371133fb399bd7fc70c131a82b64e7e20bac8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 22 Jun 2007 20:30:35 +0000 Subject: Add device files that implements org.bluez.audio.Device interface. --- audio/device.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/device.h | 73 +++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 audio/device.c create mode 100644 audio/device.h (limited to 'audio') diff --git a/audio/device.c b/audio/device.c new file mode 100644 index 00000000..88edfc70 --- /dev/null +++ b/audio/device.c @@ -0,0 +1,201 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "dbus.h" +#include "dbus-helper.h" +#include "logging.h" + +#include "device.h" + +void device_finish_sdp_transaction(struct device *device) +{ + char address[18], *addr_ptr = address; + DBusMessage *msg, *reply; + DBusError derr; + + ba2str(&device->bda, address); + + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", + "FinishRemoteServiceTransaction"); + if (!msg) { + error("Unable to allocate new method call"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(device->conn, msg, -1, + &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("FinishRemoteServiceTransaction(%s) failed: %s", + address, derr.message); + dbus_error_free(&derr); + return; + } + + dbus_message_unref(reply); +} + +static DBusHandlerResult device_get_address(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + DBusMessage *reply; + char address[18], *ptr = address; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + ba2str(&device->bda, address); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + +static DBusHandlerResult device_get_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + DBusMessageIter iter, array_iter; + struct device *device = data; + DBusMessage *reply; + const char *iface; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array_iter); + + if (device->headset && + headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } + + dbus_message_iter_close_container(&iter, &array_iter); + + return send_message_and_unref(conn, reply); +} + +static DBusMethodVTable device_methods[] = { + { "GetAddress", device_get_address, + "", "s" }, + { "GetConnectedInterfaces", device_get_connected, + "", "s" }, + { NULL, NULL, NULL, NULL } +}; + +static void device_free(struct device *device) +{ + if (device->headset) + headset_free(device); + + if (device->conn) + dbus_connection_unref(device->conn); + + if (device->adapter_path) + g_free(device->adapter_path); + + if (device->path) + g_free(device->path); + + g_free(device); +} + +static void device_unregister(DBusConnection *conn, void *data) +{ + struct device *device = data; + + info("Unregistered device path:%s", device->path); + + device_free(device); +} + +struct device * device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) +{ + struct device *device; + bdaddr_t src; + int dev_id; + + if (!conn || !path) + return NULL; + + bacpy(&src, BDADDR_ANY); + dev_id = hci_get_route(NULL); + if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) + return NULL; + + device = g_new0(struct device, 1); + + if (!dbus_connection_create_object_path(conn, path, device, + device_unregister)) { + error("D-Bus failed to register %s path", path); + device_free(device); + return NULL; + } + + if (!dbus_connection_register_interface(conn, + path, + AUDIO_DEVICE_INTERFACE, + device_methods, NULL, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_DEVICE_INTERFACE, path); + dbus_connection_destroy_object_path(conn, path); + return NULL; + } + + device->path = g_strdup(path); + bacpy(&device->bda, bda); + device->conn = dbus_connection_ref(conn); + device->adapter_path = g_malloc0(16); + snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); + + return device; +} diff --git a/audio/device.h b/audio/device.h new file mode 100644 index 00000000..1a9fd520 --- /dev/null +++ b/audio/device.h @@ -0,0 +1,73 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#include + +#include "headset.h" +#include "gateway.h" + +#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" + +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" + +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" + +#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" +#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" + +#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" + +#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" +#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" + +#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" +#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" + +/* Move these to respective .h files once they exist */ +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" +#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" +#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" +struct sink; +struct source; +struct control; +struct target; + +struct device { + DBusConnection *conn; + char *adapter_path; + char *path; + bdaddr_t bda; + + struct headset *headset; + struct gateway *gateway; + struct sink *sink; + struct source *source; + struct control *control; + struct target *target; +}; + +struct device *device_register(DBusConnection *conn, const char *path, + bdaddr_t *bda); +void device_finish_sdp_transaction(struct device *device); -- cgit From 6e1e8ebc4affc8cd47c01bc0d930fdb205e11542 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 22 Jun 2007 20:33:51 +0000 Subject: Add new files error.{c,h} so erros can be cleanly accessed in others files. --- audio/error.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/error.h | 36 ++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 audio/error.c create mode 100644 audio/error.h (limited to 'audio') diff --git a/audio/error.c b/audio/error.c new file mode 100644 index 00000000..97ffb907 --- /dev/null +++ b/audio/error.c @@ -0,0 +1,99 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "error.h" +#include "logging.h" + +#define AUDIO_ERROR_INTERFACE "org.bluez.audio.Error" + +/* FIXME: Remove these once global error functions exist */ +static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr) +{ + DBusMessage *derr; + + if (!conn || !msg) + return DBUS_HANDLER_RESULT_HANDLED; + + derr = dbus_message_new_error(msg, name, descr); + if (!derr) { + error("Unable to allocate new error return"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + + return send_message_and_unref(conn, derr); +} + +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".InvalidArguments", + descr ? descr : "Invalid arguments in method call"); +} + +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".AlreadyConnected", + "Already connected to a device"); +} + +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotConnected", + "Not connected to any device"); +} + +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotSupported", + "The service is not supported by the remote device"); +} + +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, const char *err) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".ConnectFailed", + err); +} + +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".DoesNotExist", + "Does not exist"); +} + +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) +{ + return error_reply(conn, msg, ".NotAvailable", + "Not available"); +} + +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc) +{ + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); +} diff --git a/audio/error.h b/audio/error.h new file mode 100644 index 00000000..82f19801 --- /dev/null +++ b/audio/error.h @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#include "dbus.h" + +DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, + const char *descr); +DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_connect_failed(DBusConnection *conn, + DBusMessage *msg, const char *err); +DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, + const char *dsc); -- cgit From 8636ace20adebe32149851cf068bf4b0ecb224ee Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 22 Jun 2007 20:40:17 +0000 Subject: Add audio gateway files that implements org.bluez.audio.Gateway interface. --- audio/gateway.c | 555 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/gateway.h | 30 +++ 2 files changed, 585 insertions(+) create mode 100644 audio/gateway.c create mode 100644 audio/gateway.h (limited to 'audio') diff --git a/audio/gateway.c b/audio/gateway.c new file mode 100644 index 00000000..2ee67226 --- /dev/null +++ b/audio/gateway.c @@ -0,0 +1,555 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + +#include "dbus.h" +#include "dbus-helper.h" +#include "logging.h" +#include "manager.h" +#include "error.h" + +static gboolean disable_hfp = FALSE; +static gboolean sco_over_hci = TRUE; + +static uint32_t hs_record_id = 0; +static uint32_t hf_record_id = 0; + +static GIOChannel *hs_server = NULL; +static GIOChannel *hf_server = NULL; + +static DBusConnection *connection = NULL; + +static int gateway_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *channel; + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 gateway_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0105; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 uint32_t gateway_add_ag_record(uint8_t channel, sdp_buf_t *buf) +{ + DBusMessage *msg, *reply; + DBusError derr; + dbus_uint32_t rec_id; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", "AddServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &buf->data, buf->data_size, DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("Adding service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Invalid arguments to AddServiceRecord reply: %s", + derr.message); + dbus_message_unref(reply); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + debug("add_ag_record: got record id 0x%x", rec_id); + + return rec_id; +} + +static int gateway_remove_ag_record(uint32_t rec_id) +{ + DBusMessage *msg, *reply; + DBusError derr; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RemoveServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, msg, + -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr)) { + error("Removing service record 0x%x failed: %s", + rec_id, derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + return 0; +} + +static void send_cancel_auth(struct device *device) +{ + DBusMessage *cancel; + char addr[18], *address = addr; + const char *uuid; + + if (headset_get_type(device) == SVC_HEADSET) + uuid = HSP_AG_UUID; + else + uuid = HFP_AG_UUID; + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(&device->bda, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); +} + +static void auth_cb(DBusPendingCall *call, void *data) +{ + struct device *device = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + 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"); + send_cancel_auth(device); + } + dbus_error_free(&err); + headset_close_rfcomm(device); + } else { + char hs_address[18]; + + headset_set_state(device, HEADSET_STATE_CONNECTED); + + ba2str(&device->bda, hs_address); + + debug("Accepted headset connection from %s for %s", hs_address, + device->path); + } + + dbus_message_unref(reply); +} + +static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, + void *data) +{ + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + char hs_address[18], *address = hs_address; + const char *uuid; + struct device *device; + DBusMessage *auth; + DBusPendingCall *pending; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + device = manager_device_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; + } + + if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + if (headset_connect_rfcomm(device, cli_sk) < 0) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + return TRUE; + } + + if (chan == hs_server) { + headset_set_type(device, SVC_HEADSET); + uuid = HSP_AG_UUID; + } else { + headset_set_type(device, SVC_HANDSFREE); + uuid = HFP_AG_UUID; + } + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocate RequestAuthorization method call"); + goto failed; + } + + ba2str(&device->bda, hs_address); + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { + error("Sending of authorization request failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, auth_cb, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(auth); + + return TRUE; + +failed: + headset_close_rfcomm(device); + + return TRUE; +} + +static GIOChannel *server_socket(uint8_t *channel) +{ + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = channel ? *channel : 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) +{ + uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; + + connection = dbus_connection_ref(conn); + + hs_server = server_socket(&chan); + if (!hs_server) + return -1; + + if (gateway_hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hs_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hs_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hs_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); + + disable_hfp = no_hfp; + + sco_over_hci = sco_hci; + + if (disable_hfp) + return 0; + + chan = DEFAULT_HF_AG_CHANNEL; + + hf_server = server_socket(&chan); + if (!hf_server) + return -1; + + if (gateway_hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hf_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); + + return 0; +} + +void gateway_exit(void) +{ + if (hs_record_id) { + gateway_remove_ag_record(hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + + if (hf_record_id) { + gateway_remove_ag_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } + + dbus_connection_unref(connection); + connection = NULL; +} diff --git a/audio/gateway.h b/audio/gateway.h new file mode 100644 index 00000000..137187cb --- /dev/null +++ b/audio/gateway.h @@ -0,0 +1,30 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" + +struct gateway; + +int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); + +void gateway_exit(void); -- cgit From 22dd53fe6ef9783c3357a6da1ecedd3b01bb7003 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 22 Jun 2007 20:58:12 +0000 Subject: Update build to make use of newly create files. --- audio/Makefile.am | 3 +- audio/headset.c | 827 ++++++++++++------------------------------------------ audio/headset.h | 49 +++- audio/main.c | 6 +- audio/manager.c | 369 +++++------------------- audio/manager.h | 67 +---- audio/unix.c | 9 +- 7 files changed, 296 insertions(+), 1034 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 68648a16..84242544 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -11,7 +11,8 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ - manager.h manager.c headset.h headset.c ipc.h unix.h unix.c + manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ + error.h error.c device.h device.c gateway.h gateway.c bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ diff --git a/audio/headset.c b/audio/headset.c index 723bc1ab..59fe21e7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -53,10 +53,7 @@ #include "dbus-helper.h" #include "logging.h" #include "manager.h" -#include "headset.h" - -#define DEFAULT_HS_AG_CHANNEL 12 -#define DEFAULT_HF_AG_CHANNEL 13 +#include "error.h" #define RING_INTERVAL 3000 @@ -65,32 +62,12 @@ #define HEADSET_GAIN_SPEAKER 'S' #define HEADSET_GAIN_MICROPHONE 'M' -typedef enum { - HEADSET_EVENT_KEYPRESS, - HEADSET_EVENT_GAIN, - HEADSET_EVENT_UNKNOWN, - HEADSET_EVENT_INVALID -} headset_event_t; - -typedef enum { - HEADSET_STATE_DISCONNECTED = 0, - HEADSET_STATE_CONNECT_IN_PROGRESS, - HEADSET_STATE_CONNECTED, - HEADSET_STATE_PLAY_IN_PROGRESS, - HEADSET_STATE_PLAYING, -} headset_state_t; - -typedef enum { - SVC_HEADSET, - SVC_HANDSFREE -} hs_type; - struct pending_connect { DBusMessage *msg; GIOChannel *io; guint io_id; int sock; - struct ipc_data_cfg *cfg; + struct ipc_packet *pkt; }; struct headset { @@ -108,7 +85,7 @@ struct headset { int data_start; int data_length; - hs_type type; + headset_type_t type; headset_state_t state; struct pending_connect *pending_connect; @@ -120,17 +97,6 @@ struct headset { static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data); -static gboolean disable_hfp = FALSE; -static gboolean sco_over_hci = TRUE; - -static GIOChannel *hs_server = NULL; -static GIOChannel *hf_server = NULL; - -static uint32_t hs_record_id = 0; -static uint32_t hf_record_id = 0; - -static DBusConnection *connection = NULL; - static void pending_connect_free(struct pending_connect *c) { if (c->io) @@ -140,7 +106,7 @@ static void pending_connect_free(struct pending_connect *c) g_free(c); } -static void hs_signal_gain_setting(audio_device_t *device, const char *buf) +static void hs_signal_gain_setting(struct device *device, const char *buf) { const char *name; dbus_uint16_t gain; @@ -175,7 +141,7 @@ static void hs_signal_gain_setting(audio_device_t *device, const char *buf) return; } - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); @@ -204,7 +170,7 @@ static headset_event_t parse_headset_event(const char *buf, char *rsp, return HEADSET_EVENT_UNKNOWN; } -static void close_sco(audio_device_t *device) +static void close_sco(struct device *device) { struct headset *hs = device->headset; @@ -213,13 +179,13 @@ static void close_sco(audio_device_t *device) hs->sco = NULL; assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "Stopped", DBUS_TYPE_INVALID); } static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, - audio_device_t *device) + struct device *device) { struct headset *hs; unsigned char buf[BUF_SIZE]; @@ -279,7 +245,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, hs->ring_timer = 0; } - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); @@ -322,75 +288,8 @@ failed: return FALSE; } -static void send_cancel_auth(audio_device_t *device) -{ - DBusMessage *cancel; - char addr[18], *address = addr; - const char *uuid; - - if (device->headset->type == SVC_HEADSET) - uuid = HSP_AG_UUID; - else - uuid = HFP_AG_UUID; - - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(&device->bda, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - send_message_and_unref(connection, cancel); -} - -static void auth_callback(DBusPendingCall *call, void *data) -{ - audio_device_t *device = data; - struct headset *hs = device->headset; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - 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"); - send_cancel_auth(device); - } - dbus_error_free(&err); - g_io_channel_close(hs->rfcomm); - g_io_channel_unref(hs->rfcomm); - hs->rfcomm = NULL; - } else { - char hs_address[18]; - - g_io_add_watch(hs->rfcomm, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, device); - - ba2str(&device->bda, hs_address); - - debug("Accepted headset connection from %s for %s", hs_address, - device->object_path); - - hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, device->object_path, - AUDIO_HEADSET_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); - } - - dbus_message_unref(reply); -} - static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, - audio_device_t *device) + struct device *device) { struct headset *hs; @@ -407,7 +306,7 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static GIOError headset_send(headset_t *hs, const char *str) +static GIOError headset_send(struct headset *hs, const char *str) { GIOError err; gsize total_written, written, count; @@ -434,7 +333,7 @@ static GIOError headset_send(headset_t *hs, const char *str) } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - audio_device_t *device) + struct device *device) { struct headset *hs; int ret, sk, err, flags; @@ -467,7 +366,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } - debug("SCO socket opened for headset %s", device->object_path); + debug("SCO socket opened for headset %s", device->path); info("SCO fd=%d", sk); hs->sco = chan; @@ -480,7 +379,13 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, if (hs->pending_connect->msg) { reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(device->conn, reply); + } + else if (hs->pending_connect->pkt) { + headset_get_config(device, hs->pending_connect->sock, + hs->pending_connect->pkt); + unix_send_cfg(hs->pending_connect->sock, + hs->pending_connect->pkt); } pending_connect_free(hs->pending_connect); @@ -489,7 +394,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, fcntl(sk, F_SETFL, 0); hs->state = HEADSET_STATE_PLAYING; - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); @@ -506,7 +411,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - err_connect_failed(connection, hs->pending_connect->msg, err); + err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err)); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -518,7 +423,7 @@ failed: return FALSE; } -static int sco_connect(audio_device_t *device, struct pending_connect *c) +static int sco_connect(struct device *device, struct pending_connect *c) { struct headset *hs = device->headset; struct sockaddr_sco addr; @@ -583,7 +488,7 @@ static int sco_connect(audio_device_t *device, struct pending_connect *c) } static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, - audio_device_t *device) + struct device *device) { struct headset *hs; char hs_address[18]; @@ -620,7 +525,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, hs->pending_connect->io = NULL; hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -634,7 +539,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, reply = dbus_message_new_method_return(hs->pending_connect->msg); if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(device->conn, reply); } pending_connect_free(hs->pending_connect); @@ -643,7 +548,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - err_connect_failed(connection, hs->pending_connect->msg, err); + err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err)); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); pending_connect_free(hs->pending_connect); @@ -654,7 +559,7 @@ failed: return FALSE; } -static int rfcomm_connect(audio_device_t *device, int *err) +static int rfcomm_connect(struct device *device, int *err) { struct headset *hs = device->headset; struct sockaddr_rc addr; @@ -735,219 +640,6 @@ failed: return -1; } -static int create_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - sdp_data_t *channel; - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); - profile.version = 0x0100; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 create_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - uint16_t u16 = 0x0009; - sdp_data_t *channel, *features; - uint8_t netid = 0x01; - sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - features = sdp_data_alloc(SDP_UINT16, &u16); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); - - sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 uint32_t headset_add_ag_record(uint8_t channel, sdp_buf_t *buf) -{ - DBusMessage *msg, *reply; - DBusError derr; - dbus_uint32_t rec_id; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "AddServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf->data, buf->data_size, DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("Adding service record failed: %s", derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", - derr.message); - dbus_message_unref(reply); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - debug("add_ag_record: got record id 0x%x", rec_id); - - return rec_id; -} - -int headset_remove_ag_record(uint32_t rec_id) -{ - DBusMessage *msg, *reply; - DBusError derr; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RemoveServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", - rec_id, derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - return 0; -} - static void get_record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; @@ -957,7 +649,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; @@ -1036,8 +728,8 @@ static void get_record_reply(DBusPendingCall *call, void *data) if (rfcomm_connect(device, &err) < 0) { error("Unable to connect"); - if (c->msg) - err_connect_failed(connection, c->msg, err); + if (c->msg) + err_connect_failed(device->conn, c->msg, strerror(err)); goto failed; } @@ -1045,13 +737,13 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - finish_sdp_transaction(connection, &device->bda); + device_finish_sdp_transaction(device); return; failed_not_supported: - if (c->msg) - err_not_supported(connection, c->msg); + if (c->msg) + err_not_supported(device->conn, c->msg); failed: if (classes) sdp_list_free(classes, free); @@ -1062,18 +754,18 @@ failed: pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; - finish_sdp_transaction(connection, &device->bda); + device_finish_sdp_transaction(device); } static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; if (!hs || !hs->sco) - return err_not_connected(connection, msg); + return err_not_connected(conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1085,8 +777,8 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, hs->pending_connect) { g_io_channel_close(hs->pending_connect->io); if (hs->pending_connect->msg) - err_connect_failed(connection, hs->pending_connect->msg, - EINTR); + err_connect_failed(conn, hs->pending_connect->msg, + strerror(EINTR)); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_CONNECTED; @@ -1095,7 +787,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, close_sco(device); if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1103,7 +795,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t playing; @@ -1122,7 +814,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1130,7 +822,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; char hs_address[18]; @@ -1163,9 +855,9 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (hs->pending_connect->io_id) g_source_remove(hs->pending_connect->io_id); if (hs->pending_connect->msg) - err_connect_failed(connection, + err_connect_failed(conn, hs->pending_connect->msg, - EINTR); + strerror(EINTR)); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; } @@ -1173,9 +865,9 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_DISCONNECTED; ba2str(&device->bda, hs_address); - info("Disconnected from %s, %s", hs_address, device->object_path); + info("Disconnected from %s, %s", hs_address, device->path); - dbus_connection_emit_signal(connection, device->object_path, + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); @@ -1183,7 +875,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, hs->data_length = 0; if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1192,7 +884,7 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; DBusMessage *reply; dbus_bool_t connected; @@ -1200,12 +892,12 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - connected = headset_is_connected(device->headset); + connected = (device->headset->state >= HEADSET_STATE_CONNECTED); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1215,7 +907,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; char address[18], *addr_ptr = address; @@ -1236,9 +928,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (c->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(connection, c->msg, EHOSTDOWN); + err_connect_failed(device->conn, c->msg, + strerror(EHOSTDOWN)); else - err_not_supported(connection, c->msg); + err_not_supported(device->conn, c->msg); } dbus_error_free(&derr); goto failed; @@ -1248,37 +941,36 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { - error("Unable to get args from reply"); - if (c->msg) - err_not_supported(connection, c->msg); + if (c->msg) + err_not_supported(device->conn, c->msg); goto failed; } if (!array) { error("get_handles_reply: Unable to get handle array from reply"); if (c->msg) - err_not_supported(connection, c->msg); + err_not_supported(device->conn, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); if (c->msg) - err_not_supported(connection, c->msg); + err_not_supported(device->conn, c->msg); goto failed; } if (array_len > 1) debug("Multiple records found. Using the first one."); - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, "org.bluez.Adapter", "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - if (c->msg) - err_connect_failed(connection, c->msg, ENOMEM); + if (c->msg) + err_connect_failed(device->conn, c->msg, strerror(ENOMEM)); goto failed; } @@ -1290,10 +982,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); if (c->msg) - err_connect_failed(connection, c->msg, EIO); + err_connect_failed(device->conn, c->msg, strerror(EIO)); goto failed; } @@ -1315,7 +1007,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { DBusPendingCall *pending; - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; const char *hs_svc; const char *addr_ptr; @@ -1325,7 +1017,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, assert(hs != NULL); if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) - return err_already_connected(connection, msg); + return err_already_connected(conn, msg); hs->pending_connect = g_try_new0(struct pending_connect, 1); if (!hs->pending_connect) { @@ -1345,12 +1037,12 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; - return err_connect_failed(conn, msg, err); + return err_connect_failed(conn, msg, strerror(err)); } else return DBUS_HANDLER_RESULT_HANDLED; } - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { @@ -1372,13 +1064,13 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; hs->state = HEADSET_STATE_DISCONNECTED; dbus_message_unref(msg); - return err_connect_failed(connection, msg, EIO); + return err_connect_failed(conn, msg, strerror(EIO)); } dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); @@ -1390,7 +1082,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, static gboolean ring_timer_cb(gpointer data) { - audio_device_t *device = data; + struct device *device = data; assert(device != NULL); @@ -1403,14 +1095,14 @@ static gboolean ring_timer_cb(gpointer data) static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; assert(hs != NULL); if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); + return err_not_connected(conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1425,14 +1117,14 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(connection, msg, "Failed"); + return err_failed(conn, msg, "Failed"); } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); done: if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1441,12 +1133,12 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); + return err_not_connected(conn, msg); if (msg) { reply = dbus_message_new_method_return(msg); @@ -1464,7 +1156,7 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, done: if (reply) - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1472,18 +1164,18 @@ done: static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(connection, msg); + return err_not_connected(conn, msg); if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) - return err_already_connected(connection, msg); + return err_already_connected(conn, msg); if (hs->sco) - return err_already_connected(connection, msg); + return err_already_connected(conn, msg); c = g_try_new0(struct pending_connect, 1); if (!c) @@ -1506,7 +1198,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; @@ -1525,7 +1217,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1534,7 +1226,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, DBusMessage *msg, void *data) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; @@ -1553,7 +1245,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1562,7 +1254,7 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, DBusMessage *msg, void *data, char type) { - audio_device_t *device = data; + struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply; DBusError derr; @@ -1605,7 +1297,7 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, done: if (type == HEADSET_GAIN_SPEAKER) { hs->sp_gain = gain; - dbus_connection_emit_signal(conn, device->object_path, + dbus_connection_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "SpeakerGainChanged", DBUS_TYPE_UINT16, &gain, @@ -1613,7 +1305,7 @@ done: } else { hs->mic_gain = gain; - dbus_connection_emit_signal(conn, device->object_path, + dbus_connection_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "MicrophoneGainChanged", DBUS_TYPE_UINT16, &gain, @@ -1666,7 +1358,7 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -static void headset_set_channel(headset_t *headset, sdp_record_t *record) +static void headset_set_channel(struct headset *headset, sdp_record_t *record) { int ch; sdp_list_t *protos; @@ -1688,16 +1380,12 @@ static void headset_set_channel(headset_t *headset, sdp_record_t *record) error("Unable to get RFCOMM channel from Headset record"); } -void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc) +void headset_update(void *device, sdp_record_t *record, uint16_t svc) { + struct headset *headset = ((struct device *) device)->headset; + switch (svc) { case HANDSFREE_SVCLASS_ID: - if (disable_hfp) { - debug("Ignoring Handsfree record since HFP support" - " has been disabled"); - return; - } - if (headset->hfp_handle && (headset->hfp_handle != record->handle)) { error("More than one HFP record found on device"); @@ -1730,12 +1418,12 @@ void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc) headset_set_channel(headset, record); } -headset_t *headset_init(const char *object_path, sdp_record_t *record, +struct headset *headset_init(void *device, sdp_record_t *record, uint16_t svc) { - headset_t *headset; + struct headset *headset; - headset = g_new0(headset_t, 1); + headset = g_new0(struct headset, 1); headset->rfcomm_ch = -1; headset->sp_gain = -1; headset->mic_gain = -1; @@ -1745,13 +1433,6 @@ headset_t *headset_init(const char *object_path, sdp_record_t *record, switch (svc) { case HANDSFREE_SVCLASS_ID: - if (disable_hfp) { - debug("Ignoring Handsfree record since HFP support" - " has been disabled"); - g_free(headset); - return NULL; - } - headset->hfp_handle = record->handle; break; @@ -1766,7 +1447,8 @@ headset_t *headset_init(const char *object_path, sdp_record_t *record, } register_iface: - if (!dbus_connection_register_interface(connection, object_path, + if (!dbus_connection_register_interface(((struct device *) device)->conn, + ((struct device *) device)->path, AUDIO_HEADSET_INTERFACE, headset_methods, headset_signals, NULL)) { @@ -1780,279 +1462,120 @@ register_iface: return headset; } -void headset_free(const char *object_path) +void headset_free(void *device) { - audio_device_t *device; + struct headset *headset = ((struct device *) device)->headset; - if (!dbus_connection_get_object_user_data(connection, object_path, - (void *) &device)) - return; - - if (device->headset->state != HEADSET_STATE_DISCONNECTED) + if (headset->state != HEADSET_STATE_DISCONNECTED) hs_disconnect(NULL, NULL, device); - g_free(device->headset); - device->headset = NULL; + g_free(headset); + headset = NULL; } -static gboolean headset_server_io_cb(GIOChannel *chan, GIOCondition cond, - void *data) +int headset_get_config(void *device, int sock, struct ipc_packet *pkt) { - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; - char hs_address[18], *address = hs_address; - const char *uuid; - audio_device_t *device; - struct headset *hs; - DBusMessage *auth; - DBusPendingCall *pending; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - device = manager_headset_connected(&addr.rc_bdaddr); - if (!device) { - close(cli_sk); - return TRUE; - } - - hs = device->headset; - - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->rfcomm) { - debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; - } - - hs->rfcomm = g_io_channel_unix_new(cli_sk); - if (!hs->rfcomm) { - error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; - } - - if (chan == hs_server) { - hs->type = SVC_HEADSET; - uuid = HSP_AG_UUID; - } else { - hs->type = SVC_HANDSFREE; - uuid = HFP_AG_UUID; - } - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); - goto failed; - } - - ba2str(&device->bda, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + struct headset *headset = ((struct device *) device)->headset; + struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; + int err = EINVAL; + struct pending_connect *c; - if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { - error("Sending of authorization request failed"); - goto failed; + if (headset->sco == NULL) { + c = g_try_new0(struct pending_connect, 1); + if (c == NULL) + goto error; + c->sock = sock; + c->pkt = pkt; + if ((err = sco_connect(device, c)) < 0) + goto error; + return 0; } - dbus_pending_call_set_notify(pending, auth_callback, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); - - return TRUE; - -failed: - if (hs->rfcomm) { - g_io_channel_close(hs->rfcomm); - g_io_channel_unref(hs->rfcomm); - hs->rfcomm = NULL; - } + cfg->fd = g_io_channel_unix_get_fd(headset->sco); + cfg->fd_opt = CFG_FD_OPT_READWRITE; + cfg->encoding = 0; + cfg->bitpool = 0; + cfg->channels = 1; + cfg->pkt_len = 48; + cfg->sample_size = 2; + cfg->rate = 8000; - return TRUE; + return 0; +error: + cfg->fd = -1; + return -err; } -static GIOChannel *server_socket(uint8_t *channel) +headset_type_t headset_get_type(void *device) { - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } + struct headset *headset = ((struct device *) device)->headset; - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = channel ? *channel : 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; + return headset->type; } -gboolean headset_is_connected(headset_t *headset) +void headset_set_type(void *device, headset_type_t type) { - if (headset->state >= HEADSET_STATE_CONNECTED) - return TRUE; - else - return FALSE; + struct headset *headset = ((struct device *) device)->headset; + + headset->type = type; } -int headset_server_init(DBusConnection *conn, gboolean no_hfp, - gboolean sco_hci) +int headset_connect_rfcomm(void *device, int sock) { - uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_buf_t buf; - - connection = dbus_connection_ref(conn); + struct headset *headset = ((struct device *) device)->headset; - hs_server = server_socket(&chan); - if (!hs_server) - return -1; - - if (create_hsp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - hs_record_id = headset_add_ag_record(chan, &buf); - free(buf.data); - if (!hs_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hs_server); - hs_server = NULL; - return -1; - } - - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) headset_server_io_cb, NULL); - - disable_hfp = no_hfp; - - sco_over_hci = sco_hci; - - if (disable_hfp) - return 0; + headset->rfcomm = g_io_channel_unix_new(sock); - chan = DEFAULT_HF_AG_CHANNEL; - - hf_server = server_socket(&chan); - if (!hf_server) - return -1; - - if (create_hfp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } + return headset->rfcomm ? 0 : -EINVAL; +} - hf_record_id = headset_add_ag_record(chan, &buf); - free(buf.data); - if (!hf_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hf_server); - hs_server = NULL; - return -1; - } +int headset_close_rfcomm(void *device) +{ + struct headset *headset = ((struct device *) device)->headset; - g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) headset_server_io_cb, NULL); + g_io_channel_close(headset->rfcomm); + g_io_channel_unref(headset->rfcomm); + headset->rfcomm = NULL; return 0; } -void headset_exit(void) +void headset_set_state(void *device, headset_state_t state) { - if (hs_record_id) { - headset_remove_ag_record(hs_record_id); - hs_record_id = 0; - } + struct headset *headset = ((struct device *) device)->headset; - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; - } + switch(state) { + case HEADSET_STATE_DISCONNECTED: + case HEADSET_STATE_CONNECT_IN_PROGRESS: + break; + case HEADSET_STATE_CONNECTED: + if (headset->rfcomm) { + char hs_address[18]; - if (hf_record_id) { - headset_remove_ag_record(hf_record_id); - hf_record_id = 0; - } + g_io_add_watch(headset->rfcomm, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, device); - if (hf_server) { - g_io_channel_unref(hf_server); - hf_server = NULL; + ba2str(&((struct device *) device)->bda, hs_address); + + dbus_connection_emit_signal(((struct device *) device)->conn, + ((struct device *) device)->path, + AUDIO_HEADSET_INTERFACE, + "Connected", + DBUS_TYPE_INVALID); + } + break; + case HEADSET_STATE_PLAY_IN_PROGRESS: + case HEADSET_STATE_PLAYING: + break; } - dbus_connection_unref(connection); - connection = NULL; + headset->state = state; } -int headset_get_config(headset_t *headset, int sock, struct ipc_data_cfg *cfg) +headset_state_t headset_get_state(void *device) { - if (!sco_over_hci) - return -1; + struct headset *headset = ((struct device *) device)->headset; - if (headset->sco == NULL) - return -1; - - cfg->fd = g_io_channel_unix_get_fd(headset->sco); - cfg->fd_opt = CFG_FD_OPT_READWRITE; - cfg->encoding = 0; - cfg->bitpool = 0; - cfg->channels = 1; - cfg->pkt_len = 48; - cfg->sample_size = 2; - cfg->rate = 8000; - - return 0; + return headset->state; } diff --git a/audio/headset.h b/audio/headset.h index b420860e..470fddc8 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,10 +20,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AUDIO_HEADSET_H -#define __AUDIO_HEADSET_H - -#include #include #include @@ -33,22 +29,45 @@ #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" -typedef struct headset headset_t; +#define DEFAULT_HS_AG_CHANNEL 12 +#define DEFAULT_HF_AG_CHANNEL 13 -headset_t *headset_init(const char *object_path, sdp_record_t *record, - uint16_t svc); +typedef enum { + HEADSET_EVENT_KEYPRESS, + HEADSET_EVENT_GAIN, + HEADSET_EVENT_UNKNOWN, + HEADSET_EVENT_INVALID +} headset_event_t; + +typedef enum { + HEADSET_STATE_DISCONNECTED = 0, + HEADSET_STATE_CONNECT_IN_PROGRESS, + HEADSET_STATE_CONNECTED, + HEADSET_STATE_PLAY_IN_PROGRESS, + HEADSET_STATE_PLAYING, +} headset_state_t; -void headset_free(const char *object_path); +typedef enum { + SVC_HEADSET, + SVC_HANDSFREE +} headset_type_t; + +struct headset; + +struct headset *headset_init(void *device, sdp_record_t *record, + uint16_t svc); -void headset_update(headset_t *headset, sdp_record_t *record, uint16_t svc); +void headset_free(void *device); -gboolean headset_is_connected(headset_t *headset); +void headset_update(void *device, sdp_record_t *record, uint16_t svc); -int headset_server_init(DBusConnection *conn, gboolean disable_hfp, - gboolean sco_hci); +int headset_get_config(void *device, int sock, struct ipc_packet *pkt); -void headset_exit(void); +headset_type_t headset_get_type(void *device); +void headset_set_type(void *device, headset_type_t type); -int headset_get_config(headset_t *headset, int sock, struct ipc_data_cfg *cfg); +int headset_connect_rfcomm(void *device, int sock); +int headset_close_rfcomm(void *device); -#endif /* __AUDIO_HEADSET_H_ */ +headset_state_t headset_get_state(void *device); +void headset_set_state(void *device, headset_state_t state); diff --git a/audio/main.c b/audio/main.c index 0ca8ce87..4a32a320 100644 --- a/audio/main.c +++ b/audio/main.c @@ -40,7 +40,7 @@ /* Configuration settings */ static gboolean disable_hfp = TRUE; -static gboolean sco_hci = FALSE; +static gboolean sco_hci = TRUE; static GMainLoop *main_loop = NULL; @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) exit(1); } - if (headset_server_init(conn, disable_hfp, sco_hci) < 0) { + if (gateway_init(conn, disable_hfp, sco_hci) < 0) { error("Headset initialization failed!"); exit(1); } @@ -149,7 +149,7 @@ int main(int argc, char *argv[]) audio_exit(); - headset_exit(); + gateway_exit(); unix_exit(); diff --git a/audio/manager.c b/audio/manager.c index 7faa0490..d8872968 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -42,12 +42,11 @@ #include -#include "dbus.h" #include "dbus-helper.h" #include "logging.h" -#include "headset.h" #include "manager.h" +#include "error.h" typedef enum { HEADSET = 1 << 0, @@ -67,7 +66,7 @@ typedef enum { } audio_sdp_state_t; struct audio_sdp_data { - audio_device_t *device; + struct device *device; DBusMessage *msg; /* Method call or NULL */ @@ -79,7 +78,7 @@ struct audio_sdp_data { static DBusConnection *connection = NULL; -static audio_device_t *default_dev = NULL; +static struct device *default_dev = NULL; static GSList *devices = NULL; @@ -87,80 +86,12 @@ static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, struct audio_sdp_data *data); -/* FIXME: Remove these once global error functions exist */ -static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *descr) -{ - DBusMessage *derr; - - if (!conn || !msg) - return DBUS_HANDLER_RESULT_HANDLED; - - derr = dbus_message_new_error(msg, name, descr); - if (!derr) { - error("Unable to allocate new error return"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - return send_message_and_unref(conn, derr); -} - -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.InvalidArguments", - descr ? descr : "Invalid arguments in method call"); -} - -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.AlreadyConnected", - "Already connected to a device"); -} - -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.NotConnected", - "Not connected to any device"); -} - -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.NotSupported", - "The service is not supported by the remote device"); -} - -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, int err) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.ConnectFailed", - strerror(err)); -} - -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.DoesNotExist", - "Does not exist"); -} - -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.NotAvailable", - "Not available"); -} - -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc) -{ - return error_reply(conn, msg, "org.bluez.audio.Error.Failed", dsc); -} - -static audio_device_t *find_device(bdaddr_t *bda) +static struct device *find_device(bdaddr_t *bda) { GSList *l; for (l = devices; l != NULL; l = l->next) { - audio_device_t *device = l->data; + struct device *device = l->data; if (bacmp(&device->bda, bda) == 0) return device; } @@ -168,113 +99,25 @@ static audio_device_t *find_device(bdaddr_t *bda) return NULL; } -static DBusHandlerResult device_get_address(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - audio_device_t *device = data; - DBusMessage *reply; - char address[18], *ptr = address; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - ba2str(&device->bda, address); - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_INVALID); - - return send_message_and_unref(conn, reply); -} - -static DBusHandlerResult device_get_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - DBusMessageIter iter, array_iter; - audio_device_t *device = data; - DBusMessage *reply; - const char *iface; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array_iter); - - if (device->headset && headset_is_connected(device->headset)) { - iface = AUDIO_HEADSET_INTERFACE; - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &iface); - } - - dbus_message_iter_close_container(&iter, &array_iter); - - return send_message_and_unref(conn, reply); -} - -static DBusMethodVTable device_methods[] = { - { "GetAddress", device_get_address, - "", "s" }, - { "GetConnectedInterfaces", device_get_connected, - "", "s" }, - { NULL, NULL, NULL, NULL } -}; - -static void free_device(audio_device_t *device) -{ - g_free(device); -} - -static audio_device_t *create_device(bdaddr_t *bda) +static struct device *create_device(bdaddr_t *bda) { static int device_id = 0; - audio_device_t *device; - - device = g_new0(audio_device_t, 1); + char path[128]; - bacpy(&device->bda, bda); - - snprintf(device->object_path, sizeof(device->object_path) - 1, + snprintf(path, sizeof(path) - 1, "%s/device%d", AUDIO_MANAGER_PATH, device_id++); - return device; + return device_register(connection, path, bda); } -static void remove_device(audio_device_t *device) +static void remove_device(struct device *device) { devices = g_slist_remove(devices, device); - if (device->headset) - headset_free(device->object_path); - dbus_connection_destroy_object_path(connection, device->object_path); - g_free(device); + dbus_connection_destroy_object_path(connection, device->path); } - -static gboolean add_device(audio_device_t *device) +static gboolean add_device(struct device *device) { - if (!dbus_connection_create_object_path(connection, - device->object_path, - device, NULL)) { - error("D-Bus failed to register %s path", device->object_path); - return FALSE; - } - - if (!dbus_connection_register_interface(connection, - device->object_path, - AUDIO_DEVICE_INTERFACE, - device_methods, NULL, NULL)) { - error("Failed to register %s interface to %s", - AUDIO_DEVICE_INTERFACE, device->object_path); - dbus_connection_destroy_object_path(connection, - device->object_path); - return FALSE; - } - /* First device became default */ if (g_slist_length(devices) == 0) default_dev = device; @@ -296,7 +139,7 @@ static uint16_t get_service_uuid(const sdp_record_t *record) } memcpy(&uuid, classes->data, sizeof(uuid)); - + if (!sdp_uuid128_to_uuid(&uuid)) { error("Not a 16 bit UUID"); sdp_list_free(classes, free); @@ -304,7 +147,7 @@ static uint16_t get_service_uuid(const sdp_record_t *record) } if (uuid.type == SDP_UUID32) { - if (uuid.value.uuid32 > 0xFFFF) { + if (uuid.value.uuid32 > 0xFFFF) { error("Not a 16 bit UUID"); goto done; } @@ -318,43 +161,7 @@ done: return uuid16; } -void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba) -{ - char address[18], *addr_ptr = address; - DBusMessage *msg, *reply; - DBusError derr; - - ba2str(dba, address); - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", - "org.bluez.Adapter", - "FinishRemoteServiceTransaction"); - if (!msg) { - error("Unable to allocate new method call"); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, - &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("FinishRemoteServiceTransaction(%s) failed: %s", - address, derr.message); - dbus_error_free(&derr); - return; - } - - dbus_message_unref(reply); -} - -static void handle_record(sdp_record_t *record, audio_device_t *device) +static void handle_record(sdp_record_t *record, struct device *device) { uint16_t uuid16; @@ -364,9 +171,9 @@ static void handle_record(sdp_record_t *record, audio_device_t *device) case HEADSET_SVCLASS_ID: debug("Found Headset record"); if (device->headset) - headset_update(device->headset, record, uuid16); + headset_update(device, record, uuid16); else - device->headset = headset_init(device->object_path, + device->headset = headset_init(device, record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: @@ -375,9 +182,9 @@ static void handle_record(sdp_record_t *record, audio_device_t *device) case HANDSFREE_SVCLASS_ID: debug("Found Hansfree record"); if (device->headset) - headset_update(device->headset, record, uuid16); + headset_update(device, record, uuid16); else - device->headset = headset_init(device->object_path, + device->headset = headset_init(device, record, uuid16); break; case HANDSFREE_AGW_SVCLASS_ID: @@ -434,8 +241,8 @@ static gint record_iface_cmp(gconstpointer a, gconstpointer b) static void finish_sdp(struct audio_sdp_data *data, gboolean success) { - const char *path, *addr; - char **required; + const char *addr; + char **required = NULL; int required_len, i; DBusMessage *reply = NULL; DBusError derr; @@ -443,7 +250,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) debug("Audio service discovery completed with %s", success ? "success" : "failure"); - finish_sdp_transaction(connection, &data->device->bda); + device_finish_sdp_transaction(data->device); if (!success) goto done; @@ -508,14 +315,9 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) debug("Required interface %s not supported", iface); success = FALSE; err_not_supported(connection, data->msg); - dbus_free_string_array(required); goto done; } - dbus_free_string_array(required); - - path = data->device->object_path; - reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; @@ -532,24 +334,27 @@ update: dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, + &data->device->path, DBUS_TYPE_INVALID); if (data->device->headset) - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + dbus_connection_emit_signal(connection, + AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, + &data->device->path, DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + dbus_message_append_args(reply, DBUS_TYPE_STRING, + &data->device->path, + DBUS_TYPE_INVALID); send_message_and_unref(connection, reply); } done: - if (required) - dbus_free_string_array(required); + dbus_free_string_array(required); if (!success) - free_device(data->device); + remove_device(data->device); if (data->msg) dbus_message_unref(data->msg); g_slist_foreach(data->handles, (GFunc) g_free, NULL); @@ -575,7 +380,8 @@ static void get_record_reply(DBusPendingCall *call, error("GetRemoteServiceRecord failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(connection, data->msg, EHOSTDOWN); + err_connect_failed(connection, data->msg, + strerror(EHOSTDOWN)); else err_failed(connection, data->msg, derr.message); dbus_error_free(&derr); @@ -625,12 +431,13 @@ static void get_next_record(struct audio_sdp_data *data) char address[18], *ptr = address; dbus_uint32_t *handle; - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + msg = dbus_message_new_method_call("org.bluez", + data->device->adapter_path, "org.bluez.Adapter", "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - err_connect_failed(connection, data->msg, ENOMEM); + err_connect_failed(connection, data->msg, strerror(ENOMEM)); finish_sdp(data, FALSE); return; } @@ -649,7 +456,7 @@ static void get_next_record(struct audio_sdp_data *data) if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); - err_connect_failed(connection, data->msg, EIO); + err_connect_failed(connection, data->msg, strerror(EIO)); finish_sdp(data, FALSE); return; } @@ -687,7 +494,7 @@ static void get_handles_reply(DBusPendingCall *call, error("GetRemoteServiceHandles failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(connection, data->msg, EHOSTDOWN); + err_connect_failed(connection, data->msg, strerror(EHOSTDOWN)); else err_failed(connection, data->msg, derr.message); dbus_error_free(&derr); @@ -744,7 +551,8 @@ static DBusHandlerResult get_handles(const char *uuid, const char *ptr = address; DBusMessage *msg; - msg = dbus_message_new_method_call("org.bluez", "/org/bluez/hci0", + msg = dbus_message_new_method_call("org.bluez", + data->device->adapter_path, "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { @@ -781,7 +589,7 @@ failed: } static DBusHandlerResult resolve_services(DBusMessage *msg, - audio_device_t *device) + struct device *device) { struct audio_sdp_data *sdp_data; @@ -793,9 +601,9 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, return get_handles(GENERIC_AUDIO_UUID, sdp_data); } -audio_device_t *manager_headset_connected(bdaddr_t *bda) +struct device *manager_device_connected(bdaddr_t *bda) { - audio_device_t *device; + struct device *device; const char *path; gboolean created = FALSE; @@ -806,19 +614,19 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) if (!device) { device = create_device(bda); if (!add_device(device)) { - free_device(device); + remove_device(device); return NULL; } created = TRUE; } if (!device->headset) - device->headset = headset_init(device->object_path, NULL, 0); + device->headset = headset_init(device, NULL, 0); if (!device->headset) return NULL; - path = device->object_path; + path = device->path; if (created) { dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, @@ -847,7 +655,7 @@ audio_device_t *manager_headset_connected(bdaddr_t *bda) return device; } -static gboolean device_supports_interface(audio_device_t *device, +static gboolean device_supports_interface(struct device *device, const char *iface) { if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) @@ -873,7 +681,7 @@ static gboolean device_supports_interface(audio_device_t *device, return FALSE; } -static gboolean device_matches(audio_device_t *device, char **interfaces) +static gboolean device_matches(struct device *device, char **interfaces) { int i; @@ -895,7 +703,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, char **required; int required_len; bdaddr_t bda; - audio_device_t *device; + struct device *device; DBusMessage *reply; DBusError derr; @@ -950,7 +758,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, dbus_free_string_array(required); - path = device->object_path; + path = device->path; reply = dbus_message_new_method_return(msg); if (!reply) @@ -1015,16 +823,13 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, DBUS_TYPE_STRING_AS_STRING, &array_iter); for (l = devices; l != NULL; l = l->next) { - audio_device_t *device = l->data; - const char *path; + struct device *device = l->data; if (!device_matches(device, required)) continue; - path = device->object_path; - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &path); + DBUS_TYPE_STRING, &device->path); } dbus_message_iter_close_container(&iter, &array_iter); @@ -1036,10 +841,10 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, static gint device_path_cmp(gconstpointer a, gconstpointer b) { - const audio_device_t *device = a; + const struct device *device = a; const char *path = b; - return strcmp(device->object_path, path); + return strcmp(device->path, path); } static DBusHandlerResult am_remove_device(DBusConnection *conn, @@ -1050,7 +855,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, DBusMessage *reply; GSList *match; const char *path; - audio_device_t *device; + struct device *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -1067,9 +872,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return error_reply(connection, msg, - "org.bluez.audio.Error.DoesNotExist", - "The device does not exist"); + return err_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1092,7 +895,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, default_dev = device; } - param = default_dev ? default_dev->object_path : ""; + param = default_dev ? default_dev->path : ""; dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, @@ -1120,10 +923,10 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBusMessage *msg, void *data) { - const char *path, *address; + const char *address; DBusMessage *reply; DBusError derr; - audio_device_t *device; + struct device *device; bdaddr_t bda; dbus_error_init(&derr); @@ -1147,9 +950,7 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - path = device->object_path; - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); return send_message_and_unref(conn, reply); @@ -1160,27 +961,20 @@ static DBusHandlerResult am_default_device(DBusConnection *conn, void *data) { DBusMessage *reply; - const char *path; if (!default_dev) - return error_reply(connection, msg, - "org.bluez.audio.Error.DoesNotExist", - "There is no default device"); + return err_does_not_exist(connection, msg); if (default_dev->headset == NULL && dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, "DefaultHeadset")) - return error_reply(connection, msg, - "org.bluez.audio.Error.DoesNotExist", - "There is no default headset"); + return err_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - path = default_dev->object_path; - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, + dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_dev->path, DBUS_TYPE_INVALID); return send_message_and_unref(connection, reply); @@ -1194,7 +988,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, DBusMessage *reply; GSList *match; const char *path; - audio_device_t *device; + struct device *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -1211,9 +1005,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return error_reply(connection, msg, - "org.bluez.audio.Error.DoesNotExist", - "The device does not exist"); + return err_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1221,25 +1013,21 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, device = match->data; - path = device->object_path; - if (!dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, "ChangeDefaultHeadset")) dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultDeviceChanged", - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); else if (device->headset) dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", - DBUS_TYPE_STRING, &path, + DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); else - return error_reply(connection, msg, - "org.bluez.audio.Error.DoesNotExist", - "The headset does not exist"); + return err_does_not_exist(connection, msg); default_dev = device; return send_message_and_unref(connection, reply); @@ -1316,20 +1104,7 @@ void audio_exit(void) connection = NULL; } -int manager_get_device(int sock, uint8_t role, struct ipc_data_cfg *cfg) +struct device * manager_default_device() { - GSList *l; - - if (default_dev && default_dev->headset && - headset_is_connected(default_dev->headset)) - return headset_get_config(default_dev->headset, sock, cfg); - - for (l = devices; l != NULL; l = l->next) { - audio_device_t *dev = l->data; - - if (dev->headset && headset_is_connected(dev->headset)) - return headset_get_config(dev->headset, sock, cfg); - } - - return -1; + return default_dev; } diff --git a/audio/manager.h b/audio/manager.h index 5cdf2b23..65515483 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -21,78 +21,17 @@ * */ -#include - #include -#include "headset.h" +#include "device.h" #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" - -#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" - -#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" -#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" - -#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" -#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" - -#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" - -#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" -#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" - -#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" -#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" - -/* Move these to respective .h files once they exist */ -#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" -#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" -#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" -#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" -#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" -typedef struct gateway gateway_t; -typedef struct sink sink_t; -typedef struct source source_t; -typedef struct control control_t; -typedef struct target target_t; - -typedef struct audio_device { - char object_path[128]; - bdaddr_t bda; - - headset_t *headset; - - gateway_t *gateway; - sink_t *sink; - source_t *source; - control_t *control; - target_t *target; - -} audio_device_t; - -audio_device_t *manager_headset_connected(bdaddr_t *bda); - int audio_init(DBusConnection *conn); void audio_exit(void); -void finish_sdp_transaction(DBusConnection *conn, bdaddr_t *dba); - - -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr); -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, int err); -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc); +struct device *manager_device_connected(bdaddr_t *bda); -int manager_get_device(int sock, uint8_t role, struct ipc_data_cfg *cfg); +struct device *manager_default_device(); diff --git a/audio/unix.c b/audio/unix.c index 2dad00df..f17404b4 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -78,6 +78,7 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) { + struct device *device; struct sockaddr_un addr; socklen_t addrlen; struct ipc_packet *pkt; @@ -118,9 +119,13 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) cfg = (struct ipc_data_cfg *) pkt->data; memset(cfg, 0, sizeof(struct ipc_data_cfg)); - if (manager_get_device(clisk, pkt->role, cfg) == 0) - unix_send_cfg(clisk, pkt); + if ((device = manager_default_device())) { + if (device->headset) + headset_get_config(device, clisk, pkt); + } + if (cfg->fd != 0) + unix_send_cfg(clisk, pkt); break; case PKT_TYPE_STATUS_REQ: info("Package PKT_TYPE_STATUS_REQ"); -- cgit From 1a03aad91407d5b170787c24e32edfc2e673d76c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 22 Jun 2007 23:50:24 +0000 Subject: Add storage support and fixup messed up coding style --- audio/device.c | 289 +++++++++++-------- audio/device.h | 61 ++-- audio/error.c | 64 +++-- audio/error.h | 12 +- audio/gateway.c | 865 ++++++++++++++++++++++++++++---------------------------- audio/headset.c | 16 +- audio/manager.c | 140 +++++++-- audio/manager.h | 1 + 8 files changed, 804 insertions(+), 644 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 88edfc70..8442a87c 100644 --- a/audio/device.c +++ b/audio/device.c @@ -25,6 +25,12 @@ #include #endif +#include +#include +#include +#include +#include + #include #include @@ -35,167 +41,202 @@ #include "dbus.h" #include "dbus-helper.h" #include "logging.h" +#include "textfile.h" #include "device.h" -void device_finish_sdp_transaction(struct device *device) -{ - char address[18], *addr_ptr = address; - DBusMessage *msg, *reply; - DBusError derr; - - ba2str(&device->bda, address); - - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, - "org.bluez.Adapter", - "FinishRemoteServiceTransaction"); - if (!msg) { - error("Unable to allocate new method call"); - return; - } - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(device->conn, msg, -1, - &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("FinishRemoteServiceTransaction(%s) failed: %s", - address, derr.message); - dbus_error_free(&derr); - return; - } - - dbus_message_unref(reply); -} - static DBusHandlerResult device_get_address(DBusConnection *conn, - DBusMessage *msg, - void *data) + DBusMessage *msg, void *data) { - struct device *device = data; - DBusMessage *reply; - char address[18], *ptr = address; + struct device *device = data; + DBusMessage *reply; + char address[18], *ptr = address; - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - ba2str(&device->bda, address); + ba2str(&device->dst, address); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_INVALID); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return send_message_and_unref(conn, reply); } static DBusHandlerResult device_get_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) + DBusMessage *msg, void *data) { - DBusMessageIter iter, array_iter; - struct device *device = data; - DBusMessage *reply; - const char *iface; + DBusMessageIter iter, array_iter; + struct device *device = data; + DBusMessage *reply; + const char *iface; - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &array_iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, + &array_iter); - if (device->headset && - headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { - iface = AUDIO_HEADSET_INTERFACE; - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_STRING, &iface); - } + if (device->headset && + headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { + iface = AUDIO_HEADSET_INTERFACE; + dbus_message_iter_append_basic(&array_iter, + DBUS_TYPE_STRING, &iface); + } - dbus_message_iter_close_container(&iter, &array_iter); + dbus_message_iter_close_container(&iter, &array_iter); - return send_message_and_unref(conn, reply); + return send_message_and_unref(conn, reply); } static DBusMethodVTable device_methods[] = { - { "GetAddress", device_get_address, - "", "s" }, - { "GetConnectedInterfaces", device_get_connected, - "", "s" }, - { NULL, NULL, NULL, NULL } + { "GetAddress", device_get_address, "", "s" }, + { "GetConnectedInterfaces", device_get_connected, "", "s" }, + { NULL, NULL, NULL, NULL } }; static void device_free(struct device *device) { - if (device->headset) - headset_free(device); + if (device->headset) + headset_free(device); - if (device->conn) - dbus_connection_unref(device->conn); + if (device->conn) + dbus_connection_unref(device->conn); - if (device->adapter_path) - g_free(device->adapter_path); + if (device->adapter_path) + g_free(device->adapter_path); - if (device->path) - g_free(device->path); + if (device->path) + g_free(device->path); - g_free(device); + g_free(device); } static void device_unregister(DBusConnection *conn, void *data) { - struct device *device = data; + struct device *device = data; + + info("Unregistered device path:%s", device->path); - info("Unregistered device path:%s", device->path); + device_free(device); +} + +struct device *device_register(DBusConnection *conn, + const char *path, bdaddr_t *bda) +{ + struct device *device; + bdaddr_t src; + int dev_id; + + if (!conn || !path) + return NULL; + + bacpy(&src, BDADDR_ANY); + dev_id = hci_get_route(NULL); + if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) + return NULL; + + device = g_new0(struct device, 1); + + if (!dbus_connection_create_object_path(conn, path, device, + device_unregister)) { + error("D-Bus failed to register %s path", path); + device_free(device); + return NULL; + } + + if (!dbus_connection_register_interface(conn, path, + AUDIO_DEVICE_INTERFACE, device_methods, NULL, NULL)) { + error("Failed to register %s interface to %s", + AUDIO_DEVICE_INTERFACE, path); + dbus_connection_destroy_object_path(conn, path); + return NULL; + } + + device->path = g_strdup(path); + bacpy(&device->dst, bda); + bacpy(&device->src, &src); + device->conn = dbus_connection_ref(conn); + device->adapter_path = g_malloc0(16); + snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); + + return device; +} - device_free(device); +int device_store(struct device *device, gboolean is_default) +{ + char value[64]; + char filename[PATH_MAX + 1]; + char src_addr[18], dst_addr[18]; + int err; + + if (!device->path) + return -EINVAL; + + ba2str(&device->dst, dst_addr); + ba2str(&device->src, src_addr); + + create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (is_default) + err = textfile_put(filename, "default", dst_addr); + else { + if (device->headset) + snprintf(value, 64, "headset"); + if (device->gateway) + snprintf(value, 64, "%s:gateway", value); + if (device->sink) + snprintf(value, 64, "%s:sink", value); + if (device->source) + snprintf(value, 64, "%s:source", value); + if (device->control) + snprintf(value, 64, "%s:control", value); + if (device->target) + snprintf(value, 64, "%s:target", value); + err = textfile_put(filename, dst_addr, value); + } + + return err; } -struct device * device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) +void device_finish_sdp_transaction(struct device *device) { - struct device *device; - bdaddr_t src; - int dev_id; - - if (!conn || !path) - return NULL; - - bacpy(&src, BDADDR_ANY); - dev_id = hci_get_route(NULL); - if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) - return NULL; - - device = g_new0(struct device, 1); - - if (!dbus_connection_create_object_path(conn, path, device, - device_unregister)) { - error("D-Bus failed to register %s path", path); - device_free(device); - return NULL; - } - - if (!dbus_connection_register_interface(conn, - path, - AUDIO_DEVICE_INTERFACE, - device_methods, NULL, NULL)) { - error("Failed to register %s interface to %s", - AUDIO_DEVICE_INTERFACE, path); - dbus_connection_destroy_object_path(conn, path); - return NULL; - } - - device->path = g_strdup(path); - bacpy(&device->bda, bda); - device->conn = dbus_connection_ref(conn); - device->adapter_path = g_malloc0(16); - snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); - - return device; + char address[18], *addr_ptr = address; + DBusMessage *msg, *reply; + DBusError derr; + + ba2str(&device->dst, address); + + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", + "FinishRemoteServiceTransaction"); + if (!msg) { + error("Unable to allocate new method call"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(device->conn, + msg, -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("FinishRemoteServiceTransaction(%s) failed: %s", + address, derr.message); + dbus_error_free(&derr); + return; + } + + dbus_message_unref(reply); } diff --git a/audio/device.h b/audio/device.h index 1a9fd520..12e15806 100644 --- a/audio/device.h +++ b/audio/device.h @@ -26,48 +26,53 @@ #include "headset.h" #include "gateway.h" -#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" +#define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" -#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805F9B34FB" -#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" -#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" +#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805F9B34FB" -#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" -#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" +#define HFP_HS_UUID "0000111E-0000-1000-8000-00805F9B34FB" +#define HFP_AG_UUID "0000111F-0000-1000-8000-00805F9B34FB" -#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" +#define ADVANCED_AUDIO_UUID "0000110D-0000-1000-8000-00805F9B34FB" -#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" -#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" +#define A2DP_SOURCE_UUID "0000110A-0000-1000-8000-00805F9B34FB" +#define A2DP_SINK_UUID "0000110B-0000-1000-8000-00805F9B34FB" -#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" -#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" +#define AVRCP_REMOTE_UUID "0000110E-0000-1000-8000-00805F9B34FB" +#define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" /* Move these to respective .h files once they exist */ -#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" -#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" -#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" -#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" +#define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" +#define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" + struct sink; struct source; struct control; struct target; struct device { - DBusConnection *conn; - char *adapter_path; - char *path; - bdaddr_t bda; - - struct headset *headset; - struct gateway *gateway; - struct sink *sink; - struct source *source; - struct control *control; - struct target *target; + DBusConnection *conn; + char *adapter_path; + char *path; + bdaddr_t src; + bdaddr_t dst; + + struct headset *headset; + struct gateway *gateway; + struct sink *sink; + struct source *source; + struct control *control; + struct target *target; }; -struct device *device_register(DBusConnection *conn, const char *path, - bdaddr_t *bda); +struct device *device_register(DBusConnection *conn, + const char *path, bdaddr_t *bda); + +int device_store(struct device *device, gboolean is_default); + void device_finish_sdp_transaction(struct device *device); diff --git a/audio/error.c b/audio/error.c index 97ffb907..6bd6e08d 100644 --- a/audio/error.c +++ b/audio/error.c @@ -32,68 +32,72 @@ /* FIXME: Remove these once global error functions exist */ static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *descr) + const char *name, const char *descr) { - DBusMessage *derr; + DBusMessage *derr; - if (!conn || !msg) - return DBUS_HANDLER_RESULT_HANDLED; + if (!conn || !msg) + return DBUS_HANDLER_RESULT_HANDLED; - derr = dbus_message_new_error(msg, name, descr); - if (!derr) { - error("Unable to allocate new error return"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } + derr = dbus_message_new_error(msg, name, descr); + if (!derr) { + error("Unable to allocate new error return"); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } - return send_message_and_unref(conn, derr); + return send_message_and_unref(conn, derr); } -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr) +DBusHandlerResult err_invalid_args(DBusConnection *conn, + DBusMessage *msg, const char *descr) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".InvalidArguments", - descr ? descr : "Invalid arguments in method call"); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".InvalidArguments", + descr ? descr : "Invalid arguments in method call"); } DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".AlreadyConnected", - "Already connected to a device"); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".AlreadyConnected", + "Already connected to a device"); } DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotConnected", - "Not connected to any device"); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".NotConnected", + "Not connected to any device"); } DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotSupported", - "The service is not supported by the remote device"); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".NotSupported", + "The service is not supported by the remote device"); } DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err) + DBusMessage *msg, const char *err) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".ConnectFailed", - err); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".ConnectFailed", err); } DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".DoesNotExist", - "Does not exist"); + return error_reply(conn, msg, + AUDIO_ERROR_INTERFACE ".DoesNotExist", + "Does not exist"); } DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, ".NotAvailable", - "Not available"); + return error_reply(conn, msg, ".NotAvailable", "Not available"); } -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc) +DBusHandlerResult err_failed(DBusConnection *conn, + DBusMessage *msg, const char *dsc) { - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); } diff --git a/audio/error.h b/audio/error.h index 82f19801..de4e8982 100644 --- a/audio/error.h +++ b/audio/error.h @@ -23,14 +23,14 @@ #include "dbus.h" -DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, - const char *descr); +DBusHandlerResult err_invalid_args(DBusConnection *conn, + DBusMessage *msg, const char *descr); DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg); +DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage * msg); DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err); + DBusMessage *msg, const char *err); DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_failed(DBusConnection *conn, DBusMessage *msg, - const char *dsc); +DBusHandlerResult err_failed(DBusConnection *conn, + DBusMessage *msg, const char *dsc); diff --git a/audio/gateway.c b/audio/gateway.c index 2ee67226..7064df2c 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -62,494 +62,497 @@ static DBusConnection *connection = NULL; static int gateway_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) { - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - sdp_data_t *channel; - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); - profile.version = 0x0100; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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; + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *channel; + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 gateway_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) { - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - uint16_t u16 = 0x0009; - sdp_data_t *channel, *features; - uint8_t netid = 0x01; - sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - features = sdp_data_alloc(SDP_UINT16, &u16); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); - - sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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; + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0105; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 uint32_t gateway_add_ag_record(uint8_t channel, sdp_buf_t *buf) { - DBusMessage *msg, *reply; - DBusError derr; - dbus_uint32_t rec_id; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", "AddServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf->data, buf->data_size, DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("Adding service record failed: %s", derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", - derr.message); - dbus_message_unref(reply); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - debug("add_ag_record: got record id 0x%x", rec_id); - - return rec_id; + DBusMessage *msg, *reply; + DBusError derr; + dbus_uint32_t rec_id; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "AddServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &buf->data, buf->data_size, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, + msg, -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("Adding service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Invalid arguments to AddServiceRecord reply: %s", + derr.message); + dbus_message_unref(reply); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + debug("add_ag_record: got record id 0x%x", rec_id); + + return rec_id; } static int gateway_remove_ag_record(uint32_t rec_id) { - DBusMessage *msg, *reply; - DBusError derr; + DBusMessage *msg, *reply; + DBusError derr; - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RemoveServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RemoveServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } - dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, msg, - -1, &derr); + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, + msg, -1, &derr); - dbus_message_unref(msg); + dbus_message_unref(msg); - if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", - rec_id, derr.message); - dbus_error_free(&derr); - return 0; - } + if (dbus_error_is_set(&derr)) { + error("Removing service record 0x%x failed: %s", + rec_id, derr.message); + dbus_error_free(&derr); + return 0; + } - dbus_message_unref(reply); + dbus_message_unref(reply); - return 0; + return 0; } static void send_cancel_auth(struct device *device) { - DBusMessage *cancel; - char addr[18], *address = addr; - const char *uuid; - - if (headset_get_type(device) == SVC_HEADSET) - uuid = HSP_AG_UUID; - else - uuid = HFP_AG_UUID; - - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(&device->bda, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - send_message_and_unref(connection, cancel); + DBusMessage *cancel; + char addr[18], *address = addr; + const char *uuid; + + if (headset_get_type(device) == SVC_HEADSET) + uuid = HSP_AG_UUID; + else + uuid = HFP_AG_UUID; + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(&device->dst, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); } static void auth_cb(DBusPendingCall *call, void *data) { - struct device *device = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - 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"); - send_cancel_auth(device); - } - dbus_error_free(&err); - headset_close_rfcomm(device); - } else { - char hs_address[18]; - - headset_set_state(device, HEADSET_STATE_CONNECTED); - - ba2str(&device->bda, hs_address); - - debug("Accepted headset connection from %s for %s", hs_address, - device->path); - } - - dbus_message_unref(reply); + struct device *device = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + 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"); + send_cancel_auth(device); + } + dbus_error_free(&err); + headset_close_rfcomm(device); + } else { + char hs_address[18]; + + headset_set_state(device, HEADSET_STATE_CONNECTED); + + ba2str(&device->dst, hs_address); + + debug("Accepted headset connection from %s for %s", + hs_address, device->path); + } + + dbus_message_unref(reply); } -static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, - void *data) +static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, void *data) { - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; - char hs_address[18], *address = hs_address; - const char *uuid; - struct device *device; - DBusMessage *auth; - DBusPendingCall *pending; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - device = manager_device_connected(&addr.rc_bdaddr); - if (!device) { - close(cli_sk); - return TRUE; - } - - if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { - debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; - } - - if (headset_connect_rfcomm(device, cli_sk) < 0) { - error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; - } - - if (chan == hs_server) { - headset_set_type(device, SVC_HEADSET); - uuid = HSP_AG_UUID; - } else { - headset_set_type(device, SVC_HANDSFREE); - uuid = HFP_AG_UUID; - } - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); - goto failed; - } - - ba2str(&device->bda, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { - error("Sending of authorization request failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, auth_cb, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); - - return TRUE; + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + char hs_address[18], *address = hs_address; + const char *uuid; + struct device *device; + DBusMessage *auth; + DBusPendingCall *pending; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + device = manager_device_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; + } + + if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + if (headset_connect_rfcomm(device, cli_sk) < 0) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + return TRUE; + } + + if (chan == hs_server) { + headset_set_type(device, SVC_HEADSET); + uuid = HSP_AG_UUID; + } else { + headset_set_type(device, SVC_HANDSFREE); + uuid = HFP_AG_UUID; + } + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocate RequestAuthorization method call"); + goto failed; + } + + ba2str(&device->dst, hs_address); + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { + error("Sending of authorization request failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, auth_cb, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(auth); + + return TRUE; failed: - headset_close_rfcomm(device); + headset_close_rfcomm(device); - return TRUE; + return TRUE; } static GIOChannel *server_socket(uint8_t *channel) { - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = channel ? *channel : 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = channel ? *channel : 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; } int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) { - uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_buf_t buf; + uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; - connection = dbus_connection_ref(conn); + connection = dbus_connection_ref(conn); - hs_server = server_socket(&chan); - if (!hs_server) - return -1; + hs_server = server_socket(&chan); + if (!hs_server) + return -1; - if (gateway_hsp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } + if (gateway_hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } - hs_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hs_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hs_server); - hs_server = NULL; - return -1; - } + hs_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hs_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hs_server); + hs_server = NULL; + return -1; + } - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); - disable_hfp = no_hfp; + disable_hfp = no_hfp; - sco_over_hci = sco_hci; + sco_over_hci = sco_hci; - if (disable_hfp) - return 0; + if (disable_hfp) + return 0; - chan = DEFAULT_HF_AG_CHANNEL; + chan = DEFAULT_HF_AG_CHANNEL; - hf_server = server_socket(&chan); - if (!hf_server) - return -1; + hf_server = server_socket(&chan); + if (!hf_server) + return -1; - if (gateway_hfp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } + if (gateway_hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } - hf_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hf_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hf_server); - hs_server = NULL; - return -1; - } + hf_record_id = gateway_add_ag_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } - g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) gateway_io_cb, NULL); - return 0; + return 0; } void gateway_exit(void) { - if (hs_record_id) { - gateway_remove_ag_record(hs_record_id); - hs_record_id = 0; - } - - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; - } - - if (hf_record_id) { - gateway_remove_ag_record(hf_record_id); - hf_record_id = 0; - } - - if (hf_server) { - g_io_channel_unref(hf_server); - hf_server = NULL; - } - - dbus_connection_unref(connection); - connection = NULL; + if (hs_record_id) { + gateway_remove_ag_record(hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + + if (hf_record_id) { + gateway_remove_ag_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } + + dbus_connection_unref(connection); + connection = NULL; } diff --git a/audio/headset.c b/audio/headset.c index 59fe21e7..a57c3e93 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -460,7 +460,7 @@ static int sco_connect(struct device *device, struct pending_connect *c) memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &device->bda); + bacpy(&addr.sco_bdaddr, &device->dst); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { if (!(errno == EAGAIN || errno == EINPROGRESS)) { @@ -520,7 +520,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } - ba2str(&device->bda, hs_address); + ba2str(&device->dst, hs_address); hs->rfcomm = chan; hs->pending_connect->io = NULL; @@ -570,7 +570,7 @@ static int rfcomm_connect(struct device *device, int *err) assert(hs->pending_connect != NULL); assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - ba2str(&device->bda, address); + ba2str(&device->dst, address); debug("Connecting to %s channel %d", address, hs->rfcomm_ch); @@ -602,7 +602,7 @@ static int rfcomm_connect(struct device *device, int *err) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &device->bda); + bacpy(&addr.rc_bdaddr, &device->dst); addr.rc_channel = hs->rfcomm_ch; hs->pending_connect->io = g_io_channel_unix_new(sk); @@ -864,7 +864,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, hs->state = HEADSET_STATE_DISCONNECTED; - ba2str(&device->bda, hs_address); + ba2str(&device->dst, hs_address); info("Disconnected from %s, %s", hs_address, device->path); dbus_connection_emit_signal(device->conn, device->path, @@ -974,7 +974,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - ba2str(&device->bda, address); + ba2str(&device->dst, address); handle = array[0]; @@ -1058,7 +1058,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, else hs_svc = "hfp"; - ba2str(&device->bda, hs_address); + ba2str(&device->dst, hs_address); addr_ptr = hs_address; dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_STRING, &hs_svc, @@ -1556,7 +1556,7 @@ void headset_set_state(void *device, headset_state_t state) G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); - ba2str(&((struct device *) device)->bda, hs_address); + ba2str(&((struct device *) device)->dst, hs_address); dbus_connection_emit_signal(((struct device *) device)->conn, ((struct device *) device)->path, diff --git a/audio/manager.c b/audio/manager.c index d8872968..316dd5cc 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -32,8 +32,13 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include #include @@ -44,7 +49,7 @@ #include "dbus-helper.h" #include "logging.h" - +#include "textfile.h" #include "manager.h" #include "error.h" @@ -92,7 +97,7 @@ static struct device *find_device(bdaddr_t *bda) for (l = devices; l != NULL; l = l->next) { struct device *device = l->data; - if (bacmp(&device->bda, bda) == 0) + if (bacmp(&device->dst, bda) == 0) return device; } @@ -118,12 +123,18 @@ static void remove_device(struct device *device) static gboolean add_device(struct device *device) { + gboolean is_default; + /* First device became default */ if (g_slist_length(devices) == 0) default_dev = device; devices = g_slist_append(devices, device); + is_default = default_dev == device ? TRUE : FALSE; + + device_store(device, is_default); + return TRUE; } @@ -446,7 +457,7 @@ static void get_next_record(struct audio_sdp_data *data) data->handles = g_slist_remove(data->handles, data->handles->data); - ba2str(&data->device->bda, address); + ba2str(&data->device->dst, address); dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, DBUS_TYPE_UINT32, handle, @@ -561,7 +572,7 @@ static DBusHandlerResult get_handles(const char *uuid, goto failed; } - ba2str(&data->device->bda, address); + ba2str(&data->device->dst, address); dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, DBUS_TYPE_STRING, &uuid, @@ -658,27 +669,27 @@ struct device *manager_device_connected(bdaddr_t *bda) static gboolean device_supports_interface(struct device *device, const char *iface) { - if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) - return device->headset ? TRUE : FALSE; + if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) + return device->headset ? TRUE : FALSE; - if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) - return device->gateway ? TRUE : FALSE; + if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) + return device->gateway ? TRUE : FALSE; - if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) - return device->source ? TRUE : FALSE; + if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) + return device->source ? TRUE : FALSE; - if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) + if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) return device->sink ? TRUE : FALSE; - if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) - return device->control ? TRUE : FALSE; + if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) + return device->control ? TRUE : FALSE; - if (strcmp(iface, AUDIO_TARGET_INTERFACE) == 0) - return device->target ? TRUE : FALSE; + if (strcmp(iface, AUDIO_TARGET_INTERFACE) == 0) + return device->target ? TRUE : FALSE; - debug("Unknown interface %s", iface); + debug("Unknown interface %s", iface); - return FALSE; + return FALSE; } static gboolean device_matches(struct device *device, char **interfaces) @@ -1069,6 +1080,97 @@ static DBusSignalVTable manager_signals[] = { { NULL, NULL } }; +static void parse_stored_devices(char *key, char *value, void *data) +{ + struct device *device; + bdaddr_t dst; + char addr[18]; + char *ptr; + char ifaces[6][8]; + int len, i; + + if (!key || !value) + return; + + /* Format: XX:XX:XX:XX:XX:XX interface0:interface1:... */ + memset(addr, 0, 18); + strncpy(addr, key, 17); + str2ba(addr, &dst); + + if ((device = create_device(&dst)) == NULL) + return; + + /* Parsing the interface */ + ptr = strchr(value, ':'); + + if (!ptr) { + strncpy(ifaces[0], value, 8); + if (strcmp(ifaces[0], "headset") == 0) + device->headset = headset_init(device, NULL, 0); + } + + /* FIXME: has more than 1 interface */ + for (i = 0; ptr && i < 6; i++) { + len = ptr-value; + strncpy(ifaces[i], value, len); + value = ptr; + ptr = strchr(ptr, ':'); + } + + add_device(device); +} + +static void register_devices_stored(const char *adapter) +{ + char filename[PATH_MAX + 1]; + char *addr; + struct stat s; + bdaddr_t src; + bdaddr_t dst; + bdaddr_t default_src; + int dev_id; + + create_name(filename, PATH_MAX, STORAGEDIR, adapter, "audio"); + + str2ba(adapter, &src); + + bacpy(&default_src, BDADDR_ANY); + dev_id = hci_get_route(NULL); + if (dev_id < 0) + hci_devba(dev_id, &default_src); + + if (stat(filename, &s) == 0 && (s.st_mode & __S_IFREG)) { + textfile_foreach(filename, parse_stored_devices, &src); + addr = textfile_get(filename, "default"); + + str2ba(addr, &dst); + default_dev = find_device(&dst); + } +} + +static void register_stored(void) +{ + char dirname[PATH_MAX + 1]; + struct dirent *de; + DIR *dir; + + snprintf(dirname, PATH_MAX, "%s", STORAGEDIR); + + dir = opendir(dirname); + if (!dir) + return; + + while ((de = readdir(dir)) != NULL) { + if (!isdigit(de->d_name[0])) + continue; + + /* Device objects */ + register_devices_stored(de->d_name); + } + + closedir(dir); +} + int audio_init(DBusConnection *conn) { if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, @@ -1090,6 +1192,10 @@ int audio_init(DBusConnection *conn) connection = dbus_connection_ref(conn); + info("Registered manager path:%s", AUDIO_MANAGER_PATH); + + register_stored(); + return 0; } diff --git a/audio/manager.h b/audio/manager.h index 65515483..f21e958e 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -25,6 +25,7 @@ #include "device.h" +#define MAX_PATH_LENGTH 64 /* D-Bus path */ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -- cgit From ebe177dc2e207790113fa0f34b758a5e4549f06d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 23 Jun 2007 00:08:02 +0000 Subject: Fix default config settings --- audio/gateway.c | 9 +-------- audio/main.c | 5 ++--- 2 files changed, 3 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/gateway.c b/audio/gateway.c index 7064df2c..afdcbe01 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -49,9 +49,6 @@ #include "manager.h" #include "error.h" -static gboolean disable_hfp = FALSE; -static gboolean sco_over_hci = TRUE; - static uint32_t hs_record_id = 0; static uint32_t hf_record_id = 0; @@ -498,11 +495,7 @@ int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) gateway_io_cb, NULL); - disable_hfp = no_hfp; - - sco_over_hci = sco_hci; - - if (disable_hfp) + if (no_hfp) return 0; chan = DEFAULT_HF_AG_CHANNEL; diff --git a/audio/main.c b/audio/main.c index 4a32a320..a7c93201 100644 --- a/audio/main.c +++ b/audio/main.c @@ -38,9 +38,8 @@ #include "manager.h" -/* Configuration settings */ static gboolean disable_hfp = TRUE; -static gboolean sco_hci = TRUE; +static gboolean sco_hci = FALSE; static GMainLoop *main_loop = NULL; @@ -49,7 +48,7 @@ static void sig_term(int sig) g_main_loop_quit(main_loop); } -void read_config(const char *file) +static void read_config(const char *file) { GKeyFile *keyfile; GError *err = NULL; -- cgit From 1af3ab21273cf272b3825d49a0d199b0b0a5a623 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 23 Jun 2007 00:17:30 +0000 Subject: Don't load stored devices --- audio/manager.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 316dd5cc..aa34c94b 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1080,6 +1080,7 @@ static DBusSignalVTable manager_signals[] = { { NULL, NULL } }; +#if 0 static void parse_stored_devices(char *key, char *value, void *data) { struct device *device; @@ -1119,14 +1120,17 @@ static void parse_stored_devices(char *key, char *value, void *data) add_device(device); } +#endif static void register_devices_stored(const char *adapter) { char filename[PATH_MAX + 1]; - char *addr; struct stat s; bdaddr_t src; +#if 0 bdaddr_t dst; + char *addr; +#endif bdaddr_t default_src; int dev_id; @@ -1140,11 +1144,13 @@ static void register_devices_stored(const char *adapter) hci_devba(dev_id, &default_src); if (stat(filename, &s) == 0 && (s.st_mode & __S_IFREG)) { +#if 0 textfile_foreach(filename, parse_stored_devices, &src); addr = textfile_get(filename, "default"); str2ba(addr, &dst); default_dev = find_device(&dst); +#endif } } @@ -1210,7 +1216,7 @@ void audio_exit(void) connection = NULL; } -struct device * manager_default_device() +struct device *manager_default_device() { return default_dev; } -- cgit From 2cc84f09868d94aad737853b12ab41f4b83a1db9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Sat, 23 Jun 2007 18:21:23 +0000 Subject: Fix storage code. --- audio/device.c | 36 ++++++++++++++++-------------------- audio/manager.c | 42 +++++++++++------------------------------- 2 files changed, 27 insertions(+), 51 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 8442a87c..785d0115 100644 --- a/audio/device.c +++ b/audio/device.c @@ -83,7 +83,7 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, &array_iter); if (device->headset && - headset_get_state(device->headset) >= HEADSET_STATE_CONNECTED) { + headset_get_state(device) >= HEADSET_STATE_CONNECTED) { iface = AUDIO_HEADSET_INTERFACE; dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &iface); @@ -173,7 +173,6 @@ int device_store(struct device *device, gboolean is_default) char value[64]; char filename[PATH_MAX + 1]; char src_addr[18], dst_addr[18]; - int err; if (!device->path) return -EINVAL; @@ -185,24 +184,21 @@ int device_store(struct device *device, gboolean is_default) create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (is_default) - err = textfile_put(filename, "default", dst_addr); - else { - if (device->headset) - snprintf(value, 64, "headset"); - if (device->gateway) - snprintf(value, 64, "%s:gateway", value); - if (device->sink) - snprintf(value, 64, "%s:sink", value); - if (device->source) - snprintf(value, 64, "%s:source", value); - if (device->control) - snprintf(value, 64, "%s:control", value); - if (device->target) - snprintf(value, 64, "%s:target", value); - err = textfile_put(filename, dst_addr, value); - } - - return err; + textfile_put(filename, "default", dst_addr); + if (device->headset) + snprintf(value, 64, "headset"); + else if (device->gateway) + snprintf(value, 64, "gateway"); + else if (device->sink) + snprintf(value, 64, "sink"); + else if (device->source) + snprintf(value, 64, "source"); + else if (device->control) + snprintf(value, 64, "control"); + else + snprintf(value, 64, "target"); + + return textfile_put(filename, dst_addr, value); } void device_finish_sdp_transaction(struct device *device) diff --git a/audio/manager.c b/audio/manager.c index aa34c94b..fca8d47a 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -123,17 +123,12 @@ static void remove_device(struct device *device) static gboolean add_device(struct device *device) { - gboolean is_default; - /* First device became default */ if (g_slist_length(devices) == 0) default_dev = device; devices = g_slist_append(devices, device); - is_default = default_dev == device ? TRUE : FALSE; - - device_store(device, is_default); return TRUE; } @@ -174,6 +169,7 @@ done: static void handle_record(sdp_record_t *record, struct device *device) { + gboolean is_default; uint16_t uuid16; uuid16 = get_service_uuid(record); @@ -217,6 +213,10 @@ static void handle_record(sdp_record_t *record, struct device *device) debug("Unrecognized UUID: 0x%04X", uuid16); break; } + + is_default = (default_dev == device) ? TRUE : FALSE; + + device_store(device, is_default); } static gint record_iface_cmp(gconstpointer a, gconstpointer b) @@ -1041,6 +1041,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, return err_does_not_exist(connection, msg); default_dev = device; + device_store(device, TRUE); return send_message_and_unref(connection, reply); } @@ -1080,20 +1081,17 @@ static DBusSignalVTable manager_signals[] = { { NULL, NULL } }; -#if 0 static void parse_stored_devices(char *key, char *value, void *data) { struct device *device; bdaddr_t dst; char addr[18]; - char *ptr; - char ifaces[6][8]; - int len, i; - if (!key || !value) + if (!key || !value || strcmp(key, "default") == 0) return; /* Format: XX:XX:XX:XX:XX:XX interface0:interface1:... */ + info("Loading device %s", key); memset(addr, 0, 18); strncpy(addr, key, 17); str2ba(addr, &dst); @@ -1101,36 +1099,19 @@ static void parse_stored_devices(char *key, char *value, void *data) if ((device = create_device(&dst)) == NULL) return; - /* Parsing the interface */ - ptr = strchr(value, ':'); - - if (!ptr) { - strncpy(ifaces[0], value, 8); - if (strcmp(ifaces[0], "headset") == 0) - device->headset = headset_init(device, NULL, 0); - } - - /* FIXME: has more than 1 interface */ - for (i = 0; ptr && i < 6; i++) { - len = ptr-value; - strncpy(ifaces[i], value, len); - value = ptr; - ptr = strchr(ptr, ':'); - } + if (strcmp(value, "headset") == 0) + device->headset = headset_init(device, NULL, 0); add_device(device); } -#endif static void register_devices_stored(const char *adapter) { char filename[PATH_MAX + 1]; struct stat s; bdaddr_t src; -#if 0 bdaddr_t dst; char *addr; -#endif bdaddr_t default_src; int dev_id; @@ -1144,13 +1125,12 @@ static void register_devices_stored(const char *adapter) hci_devba(dev_id, &default_src); if (stat(filename, &s) == 0 && (s.st_mode & __S_IFREG)) { -#if 0 textfile_foreach(filename, parse_stored_devices, &src); addr = textfile_get(filename, "default"); str2ba(addr, &dst); default_dev = find_device(&dst); -#endif + free(addr); } } -- cgit From 32274fc77d459ab72868d15bf1f65f8fa74c6013 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 23 Jun 2007 18:57:42 +0000 Subject: Handle systems without a default headset --- audio/manager.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index fca8d47a..65a27f5f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1126,7 +1126,10 @@ static void register_devices_stored(const char *adapter) if (stat(filename, &s) == 0 && (s.st_mode & __S_IFREG)) { textfile_foreach(filename, parse_stored_devices, &src); + addr = textfile_get(filename, "default"); + if (!addr) + return; str2ba(addr, &dst); default_dev = find_device(&dst); -- cgit From 0a3d6e899851ff7e43b502373076d85b5f016f09 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 23 Jun 2007 19:29:13 +0000 Subject: Fixing the default device selection --- audio/manager.c | 59 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 26 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 65a27f5f..0ea8a99b 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -117,19 +117,25 @@ static struct device *create_device(bdaddr_t *bda) static void remove_device(struct device *device) { + if (device == default_dev) { + debug("Removing default device"); + default_dev = NULL; + } + devices = g_slist_remove(devices, device); + dbus_connection_destroy_object_path(connection, device->path); } static gboolean add_device(struct device *device) { - /* First device became default */ - if (g_slist_length(devices) == 0) + if (default_dev == NULL && g_slist_length(devices) == 0) { + debug("Selecting default device"); default_dev = device; + } devices = g_slist_append(devices, device); - return TRUE; } @@ -978,7 +984,7 @@ static DBusHandlerResult am_default_device(DBusConnection *conn, if (default_dev->headset == NULL && dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, - "DefaultHeadset")) + "DefaultHeadset")) return err_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); @@ -1085,18 +1091,15 @@ static void parse_stored_devices(char *key, char *value, void *data) { struct device *device; bdaddr_t dst; - char addr[18]; if (!key || !value || strcmp(key, "default") == 0) return; - /* Format: XX:XX:XX:XX:XX:XX interface0:interface1:... */ - info("Loading device %s", key); - memset(addr, 0, 18); - strncpy(addr, key, 17); - str2ba(addr, &dst); + info("Loading device %s (%s)", key, value); + str2ba(key, &dst); - if ((device = create_device(&dst)) == NULL) + device = create_device(&dst); + if (!device) return; if (strcmp(value, "headset") == 0) @@ -1108,33 +1111,37 @@ static void parse_stored_devices(char *key, char *value, void *data) static void register_devices_stored(const char *adapter) { char filename[PATH_MAX + 1]; - struct stat s; + struct stat st; + struct device *device; bdaddr_t src; bdaddr_t dst; char *addr; - bdaddr_t default_src; - int dev_id; create_name(filename, PATH_MAX, STORAGEDIR, adapter, "audio"); str2ba(adapter, &src); - bacpy(&default_src, BDADDR_ANY); - dev_id = hci_get_route(NULL); - if (dev_id < 0) - hci_devba(dev_id, &default_src); + if (stat(filename, &st) < 0) + return; + + if (!(st.st_mode & __S_IFREG)) + return; + + textfile_foreach(filename, parse_stored_devices, &src); - if (stat(filename, &s) == 0 && (s.st_mode & __S_IFREG)) { - textfile_foreach(filename, parse_stored_devices, &src); + addr = textfile_get(filename, "default"); + if (!addr) + return; - addr = textfile_get(filename, "default"); - if (!addr) - return; + str2ba(addr, &dst); + device = find_device(&dst); - str2ba(addr, &dst); - default_dev = find_device(&dst); - free(addr); + if (device) { + info("Setting %s as default device", addr); + default_dev = device; } + + free(addr); } static void register_stored(void) -- cgit From c205cc889023e733e56365e82dfbaad2d2b46942 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 27 Jun 2007 16:58:19 +0000 Subject: Make headset also connect rfcomm when it demands, remove asserts and some code cleanups. --- audio/headset.c | 613 ++++++++++++++++++++++++++++---------------------------- audio/headset.h | 4 +- 2 files changed, 310 insertions(+), 307 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a57c3e93..1e8b640a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -96,6 +95,7 @@ struct headset { static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data); +static int rfcomm_connect(struct device *device, struct pending_connect *c); static void pending_connect_free(struct pending_connect *c) { @@ -177,7 +177,6 @@ static void close_sco(struct device *device) g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; - assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "Stopped", @@ -311,8 +310,6 @@ static GIOError headset_send(struct headset *hs, const char *str) GIOError err; gsize total_written, written, count; - assert(hs != NULL); - if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { error("headset_send: the headset is not connected"); return G_IO_ERROR_UNKNOWN; @@ -346,11 +343,6 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs = device->headset; - assert(hs != NULL); - assert(hs->pending_connect != NULL); - assert(hs->sco == NULL); - assert(hs->state == HEADSET_STATE_PLAY_IN_PROGRESS); - sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); @@ -411,13 +403,16 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err)); + if (hs->pending_connect->msg) + err_connect_failed(device->conn, hs->pending_connect->msg, + strerror(err)); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); + if (hs->pending_connect->pkt) + unix_send_cfg(hs->pending_connect->sock, + hs->pending_connect->pkt); pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; - - assert(hs->rfcomm); hs->state = HEADSET_STATE_CONNECTED; return FALSE; @@ -492,7 +487,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, { struct headset *hs; char hs_address[18]; - int sk, ret, err; + int sk, ret, err = 0; socklen_t len; if (cond & G_IO_NVAL) @@ -500,11 +495,6 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, hs = device->headset; - assert(hs != NULL); - assert(hs->pending_connect != NULL); - assert(hs->rfcomm == NULL); - assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); @@ -529,6 +519,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); + device_store(device, FALSE); debug("Connected to %s", hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, @@ -541,6 +532,11 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, if (reply) send_message_and_unref(device->conn, reply); } + else if (hs->pending_connect->pkt) { + if (sco_connect(device, hs->pending_connect) < 0) + goto failed; + return FALSE; + } pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; @@ -548,98 +544,24 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - err_connect_failed(device->conn, hs->pending_connect->msg, strerror(err)); + if (hs->pending_connect->msg) + err_connect_failed(device->conn, hs->pending_connect->msg, + strerror(err)); + if (hs->pending_connect->pkt) + unix_send_cfg(hs->pending_connect->sock, + hs->pending_connect->pkt); if (hs->pending_connect->io) g_io_channel_close(hs->pending_connect->io); + if (hs->rfcomm) + hs->state = HEADSET_STATE_CONNECTED; + else + hs->state = HEADSET_STATE_DISCONNECTED; pending_connect_free(hs->pending_connect); hs->pending_connect = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; - return FALSE; } -static int rfcomm_connect(struct device *device, int *err) -{ - struct headset *hs = device->headset; - struct sockaddr_rc addr; - char address[18]; - int sk; - - assert(hs != NULL); - assert(hs->pending_connect != NULL); - assert(hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS); - - ba2str(&device->dst, address); - - debug("Connecting to %s channel %d", address, hs->rfcomm_ch); - - sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) { - if (err) - *err = errno; - error("socket: %s (%d)", strerror(errno), errno); - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - if (err) - *err = errno; - error("bind: %s (%d)", strerror(errno), errno); - goto failed; - } - - if (set_nonblocking(sk) < 0) { - if (err) - *err = errno; - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &device->dst); - addr.rc_channel = hs->rfcomm_ch; - - hs->pending_connect->io = g_io_channel_unix_new(sk); - if (!hs->pending_connect->io) { - if (err) - *err = ENOMEM; - error("channel_unix_new failed in rfcomm connect"); - goto failed; - } - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - if (!(errno == EAGAIN || errno == EINPROGRESS)) { - if (err) - *err = errno; - error("connect() failed: %s (%d)", strerror(errno), - errno); - goto failed; - } - - debug("Connect in progress"); - - g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) rfcomm_connect_cb, device); - } else { - debug("Connect succeeded with first try"); - rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device); - } - - return 0; - -failed: - if (!hs->pending_connect->io && sk >= 0) - close(sk); - - return -1; -} - static void get_record_reply(DBusPendingCall *call, void *data) { DBusMessage *reply; @@ -653,10 +575,6 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct headset *hs = device->headset; struct pending_connect *c; - assert(hs != NULL); - assert(hs->pending_connect); - assert(!hs->rfcomm); - c = hs->pending_connect; reply = dbus_pending_call_steal_reply(call); @@ -726,10 +644,11 @@ static void get_record_reply(DBusPendingCall *call, void *data) hs->rfcomm_ch = ch; - if (rfcomm_connect(device, &err) < 0) { + if ((err = rfcomm_connect(device, NULL)) < 0) { error("Unable to connect"); if (c->msg) - err_connect_failed(device->conn, c->msg, strerror(err)); + err_connect_failed(device->conn, c->msg, + strerror(-err)); goto failed; } @@ -757,6 +676,228 @@ failed: device_finish_sdp_transaction(device); } +static void get_handles_reply(DBusPendingCall *call, void *data) +{ + DBusMessage *msg = NULL, *reply; + DBusPendingCall *pending; + DBusError derr; + struct device *device = data; + struct headset *hs = device->headset; + struct pending_connect *c; + char address[18], *addr_ptr = address; + dbus_uint32_t *array = NULL; + dbus_uint32_t handle; + int array_len; + + c = hs->pending_connect; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("GetRemoteServiceHandles failed: %s", derr.message); + if (c->msg) { + if (dbus_error_has_name(&derr, + "org.bluez.Error.ConnectionAttemptFailed")) + err_connect_failed(device->conn, c->msg, + strerror(EHOSTDOWN)); + else + err_not_supported(device->conn, c->msg); + } + dbus_error_free(&derr); + goto failed; + } + + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, + &array, &array_len, + DBUS_TYPE_INVALID)) { + error("Unable to get args from reply"); + if (c->msg) + err_not_supported(device->conn, c->msg); + goto failed; + } + + if (!array) { + error("get_handles_reply: Unable to get handle array from reply"); + if (c->msg) + err_not_supported(device->conn, c->msg); + goto failed; + } + + if (array_len < 1) { + debug("No record handles found"); + if (c->msg) + err_not_supported(device->conn, c->msg); + goto failed; + } + + if (array_len > 1) + debug("Multiple records found. Using the first one."); + + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", + "GetRemoteServiceRecord"); + if (!msg) { + error("Unable to allocate new method call"); + if (c->msg) + err_connect_failed(device->conn, c->msg, strerror(ENOMEM)); + goto failed; + } + + ba2str(&device->dst, address); + + handle = array[0]; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { + error("Sending GetRemoteServiceRecord failed"); + if (c->msg) + err_connect_failed(device->conn, c->msg, strerror(EIO)); + goto failed; + } + + dbus_pending_call_set_notify(pending, get_record_reply, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(msg); + dbus_message_unref(reply); + + return; + +failed: + if (msg) + dbus_message_unref(msg); + dbus_message_unref(reply); + hs_disconnect(NULL, NULL, device); +} + +static int get_handles(struct device *device) +{ + DBusPendingCall *pending; + struct headset *hs = device->headset; + const char *hs_svc; + const char *addr_ptr; + char hs_address[18]; + DBusMessage *msg; + + msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", + "GetRemoteServiceHandles"); + if (!msg) { + error("Could not create a new dbus message"); + return -EINVAL; + } + + if (hs->type == SVC_HEADSET) + hs_svc = "hsp"; + else + hs_svc = "hfp"; + + ba2str(&device->dst, hs_address); + addr_ptr = hs_address; + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_STRING, &hs_svc, + DBUS_TYPE_INVALID); + + hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; + if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { + error("Sending GetRemoteServiceHandles failed"); + dbus_message_unref(msg); + return -EIO; + } + + dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(msg); + + return 0; +} + +static int rfcomm_connect(struct device *device, struct pending_connect *c) +{ + struct headset *hs = device->headset; + struct sockaddr_rc addr; + char address[18]; + int sk, err; + + if (c != NULL) { + hs->pending_connect = c; + hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + + if (hs->state != HEADSET_STATE_DISCONNECTED) + return -EBUSY; + + if (hs->rfcomm_ch < 0) + return get_handles(device); + } + + ba2str(&device->dst, address); + + debug("Connecting to %s channel %d", address, hs->rfcomm_ch); + + sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sk < 0) { + err = errno; + error("socket: %s (%d)", strerror(err), err); + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = 0; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + error("bind: %s (%d)", strerror(errno), errno); + goto failed; + } + + if (set_nonblocking(sk) < 0) { + err = errno; + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, &device->dst); + addr.rc_channel = hs->rfcomm_ch; + + hs->pending_connect->io = g_io_channel_unix_new(sk); + if (!hs->pending_connect->io) { + err = ENOMEM; + error("channel_unix_new failed in rfcomm connect"); + goto failed; + } + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect() failed: %s (%d)", strerror(err), err); + goto failed; + } + + debug("Connect in progress"); + + g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, + (GIOFunc) rfcomm_connect_cb, device); + } else { + debug("Connect succeeded with first try"); + rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device); + } + + return 0; + +failed: + if (!hs->pending_connect->io && sk >= 0) + close(sk); + + return -err; +} + static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -800,8 +941,6 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, DBusMessage *reply; dbus_bool_t playing; - assert(hs); - reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; @@ -815,7 +954,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, DBUS_TYPE_INVALID); send_message_and_unref(conn, reply); - + return DBUS_HANDLER_RESULT_HANDLED; } @@ -827,8 +966,6 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, DBusMessage *reply = NULL; char hs_address[18]; - assert(hs); - if (msg) { reply = dbus_message_new_method_return(msg); if (!reply) @@ -876,7 +1013,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (reply) send_message_and_unref(conn, reply); - + return DBUS_HANDLER_RESULT_HANDLED; } @@ -898,194 +1035,43 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBUS_TYPE_INVALID); send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; -} -static void get_handles_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *msg = NULL, *reply; - DBusPendingCall *pending; - DBusError derr; - struct device *device = data; - struct headset *hs = device->headset; - struct pending_connect *c; - char address[18], *addr_ptr = address; - dbus_uint32_t *array = NULL; - dbus_uint32_t handle; - int array_len; - - assert(hs != NULL); - assert(hs->pending_connect); - - c = hs->pending_connect; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("GetRemoteServiceHandles failed: %s", derr.message); - if (c->msg) { - if (dbus_error_has_name(&derr, - "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(device->conn, c->msg, - strerror(EHOSTDOWN)); - else - err_not_supported(device->conn, c->msg); - } - dbus_error_free(&derr); - goto failed; - } - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &array, &array_len, - DBUS_TYPE_INVALID)) { - error("Unable to get args from reply"); - if (c->msg) - err_not_supported(device->conn, c->msg); - goto failed; - } - - if (!array) { - error("get_handles_reply: Unable to get handle array from reply"); - if (c->msg) - err_not_supported(device->conn, c->msg); - goto failed; - } - - if (array_len < 1) { - debug("No record handles found"); - if (c->msg) - err_not_supported(device->conn, c->msg); - goto failed; - } - - if (array_len > 1) - debug("Multiple records found. Using the first one."); - - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceRecord"); - if (!msg) { - error("Unable to allocate new method call"); - if (c->msg) - err_connect_failed(device->conn, c->msg, strerror(ENOMEM)); - goto failed; - } - - ba2str(&device->dst, address); - - handle = array[0]; - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { - error("Sending GetRemoteServiceRecord failed"); - if (c->msg) - err_connect_failed(device->conn, c->msg, strerror(EIO)); - goto failed; - } - - dbus_pending_call_set_notify(pending, get_record_reply, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); - dbus_message_unref(reply); - - return; - -failed: - if (msg) - dbus_message_unref(msg); - dbus_message_unref(reply); - hs_disconnect(NULL, NULL, device); + return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { - DBusPendingCall *pending; struct device *device = data; struct headset *hs = device->headset; - const char *hs_svc; - const char *addr_ptr; - char hs_address[18]; + struct pending_connect *c; int err; - assert(hs != NULL); - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) return err_already_connected(conn, msg); - hs->pending_connect = g_try_new0(struct pending_connect, 1); - if (!hs->pending_connect) { + c = g_try_new0(struct pending_connect, 1); + if (!c) { error("Out of memory when allocating struct pending_connect"); return DBUS_HANDLER_RESULT_NEED_MEMORY; } - hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; - - hs->pending_connect->msg = msg ? dbus_message_ref(msg) : NULL; - - hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; - - if (hs->rfcomm_ch > 0) { - if (rfcomm_connect(device, &err) < 0) { - error("Unable to connect"); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; - return err_connect_failed(conn, msg, strerror(err)); - } else - return DBUS_HANDLER_RESULT_HANDLED; - } - - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceHandles"); - if (!msg) { - error("Could not create a new dbus message"); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - if (hs->type == SVC_HEADSET) - hs_svc = "hsp"; - else - hs_svc = "hfp"; - - ba2str(&device->dst, hs_address); - addr_ptr = hs_address; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_STRING, &hs_svc, - DBUS_TYPE_INVALID); + c->msg = dbus_message_ref(msg); - if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { - error("Sending GetRemoteServiceHandles failed"); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; - dbus_message_unref(msg); - return err_connect_failed(conn, msg, strerror(EIO)); - } - - dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); + if ((err = rfcomm_connect(device, c)) < 0) + goto error; - return DBUS_HANDLER_RESULT_HANDLED;; + return DBUS_HANDLER_RESULT_HANDLED; +error: + if (c) + pending_connect_free(c); + return err_connect_failed(conn, msg, strerror(-err)); } static gboolean ring_timer_cb(gpointer data) { struct device *device = data; - assert(device != NULL); - if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) error("Sending RING failed"); @@ -1099,8 +1085,6 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, struct headset *hs = device->headset; DBusMessage *reply = NULL; - assert(hs != NULL); - if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); @@ -1167,6 +1151,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; + int err; if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); @@ -1183,13 +1168,11 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, c->msg = msg ? dbus_message_ref(msg) : NULL; - if (sco_connect(device, c) < 0) - goto failed; - - return 0; -failed: - if (c) + err = sco_connect(device, c); + if (err < 0) { pending_connect_free(c); + return err_failed(conn, msg, strerror(-err)); + } return DBUS_HANDLER_RESULT_HANDLED; } @@ -1203,8 +1186,6 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, DBusMessage *reply; dbus_uint16_t gain; - assert(hs); - if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0) return err_not_available(conn, msg); @@ -1218,7 +1199,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, DBUS_TYPE_INVALID); send_message_and_unref(conn, reply); - + return DBUS_HANDLER_RESULT_HANDLED; } @@ -1231,8 +1212,6 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, DBusMessage *reply; dbus_uint16_t gain; - assert(hs); - if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0) return err_not_available(conn, msg); @@ -1246,7 +1225,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, DBUS_TYPE_INVALID); send_message_and_unref(conn, reply); - + return DBUS_HANDLER_RESULT_HANDLED; } @@ -1261,8 +1240,6 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, dbus_uint16_t gain; char str[13]; - assert(hs); - if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); @@ -1313,7 +1290,7 @@ done: } send_message_and_unref(conn, reply); - + return DBUS_HANDLER_RESULT_HANDLED; } @@ -1419,12 +1396,12 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) } struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc) + uint16_t svc, int channel) { struct headset *headset; headset = g_new0(struct headset, 1); - headset->rfcomm_ch = -1; + headset->rfcomm_ch = channel; headset->sp_gain = -1; headset->mic_gain = -1; @@ -1446,19 +1423,17 @@ struct headset *headset_init(void *device, sdp_record_t *record, return NULL; } + headset_set_channel(headset, record); register_iface: if (!dbus_connection_register_interface(((struct device *) device)->conn, - ((struct device *) device)->path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) { + ((struct device *) device)->path, + AUDIO_HEADSET_INTERFACE, + headset_methods, + headset_signals, NULL)) { g_free(headset); return NULL; } - if (record) - headset_set_channel(headset, record); - return headset; } @@ -1466,8 +1441,15 @@ void headset_free(void *device) { struct headset *headset = ((struct device *) device)->headset; - if (headset->state != HEADSET_STATE_DISCONNECTED) - hs_disconnect(NULL, NULL, device); + if (headset->sco) { + g_io_channel_close(headset->sco); + g_io_channel_unref(headset->sco); + } + + if (headset->rfcomm) { + g_io_channel_close(headset->rfcomm); + g_io_channel_unref(headset->rfcomm); + } g_free(headset); headset = NULL; @@ -1480,7 +1462,17 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) int err = EINVAL; struct pending_connect *c; - if (headset->sco == NULL) { + if (headset->rfcomm == NULL) { + c = g_try_new0(struct pending_connect, 1); + if (c == NULL) + goto error; + c->sock = sock; + c->pkt = pkt; + if ((err = rfcomm_connect(device, c)) < 0) + goto error; + return 0; + } + else if (headset->sco == NULL) { c = g_try_new0(struct pending_connect, 1); if (c == NULL) goto error; @@ -1502,6 +1494,8 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) return 0; error: + if (c) + pending_connect_free(c); cfg->fd = -1; return -err; } @@ -1579,3 +1573,10 @@ headset_state_t headset_get_state(void *device) return headset->state; } + +int headset_get_channel(void *device) +{ + struct headset *headset = ((struct device *) device)->headset; + + return headset->rfcomm_ch; +} diff --git a/audio/headset.h b/audio/headset.h index 470fddc8..8ef08249 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -55,7 +55,7 @@ typedef enum { struct headset; struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc); + uint16_t svc, int channel); void headset_free(void *device); @@ -71,3 +71,5 @@ int headset_close_rfcomm(void *device); headset_state_t headset_get_state(void *device); void headset_set_state(void *device, headset_state_t state); + +int headset_get_channel(void *device); -- cgit From 2a2c204cd0e4bcf0a603ba72be9a50203a817b54 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 27 Jun 2007 17:03:07 +0000 Subject: Make storage to also save rfcomm channel, minor fixes to alsa plugin and fixes for service shutdown. --- audio/device.c | 2 +- audio/manager.c | 35 +++++++++++++++++++++++++---------- audio/pcm_bluetooth.c | 11 +++++++---- audio/unix.c | 1 - 4 files changed, 33 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 785d0115..884a2661 100644 --- a/audio/device.c +++ b/audio/device.c @@ -186,7 +186,7 @@ int device_store(struct device *device, gboolean is_default) if (is_default) textfile_put(filename, "default", dst_addr); if (device->headset) - snprintf(value, 64, "headset"); + snprintf(value, 64, "headset#%d", headset_get_channel(device)); else if (device->gateway) snprintf(value, 64, "gateway"); else if (device->sink) diff --git a/audio/manager.c b/audio/manager.c index 0ea8a99b..8ca57100 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -187,7 +186,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - record, uuid16); + record, uuid16, -1); break; case HEADSET_AGW_SVCLASS_ID: debug("Found Headset AG record"); @@ -198,7 +197,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - record, uuid16); + record, uuid16, -1); break; case HANDSFREE_AGW_SVCLASS_ID: debug("Found Handsfree AG record"); @@ -638,7 +637,7 @@ struct device *manager_device_connected(bdaddr_t *bda) } if (!device->headset) - device->headset = headset_init(device, NULL, 0); + device->headset = headset_init(device, NULL, 0, -1); if (!device->headset) return NULL; @@ -1102,8 +1101,15 @@ static void parse_stored_devices(char *key, char *value, void *data) if (!device) return; - if (strcmp(value, "headset") == 0) - device->headset = headset_init(device, NULL, 0); + if (strncmp(value, "headset", strlen("headset")) == 0) { + int channel = -1; + char *ptr; + + if ((ptr = strchr(value, '#'))) + channel = strtol(ptr+1, NULL, 10); + + device->headset = headset_init(device, NULL, 0, channel); + } add_device(device); } @@ -1167,10 +1173,21 @@ static void register_stored(void) closedir(dir); } +static void manager_unregister(DBusConnection *conn, void *data) +{ + info("Unregistered manager path"); + + if (devices) { + g_slist_foreach(devices, (GFunc)remove_device, NULL); + g_slist_free(devices); + devices = NULL; + } +} + int audio_init(DBusConnection *conn) { if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, - NULL, NULL)) { + NULL, manager_unregister)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); return -1; } @@ -1197,9 +1214,7 @@ int audio_init(DBusConnection *conn) void audio_exit(void) { - g_slist_foreach(devices, (GFunc) remove_device, NULL); - g_slist_free(devices); - devices = NULL; + dbus_connection_destroy_object_path(connection, AUDIO_MANAGER_PATH); dbus_connection_unref(connection); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 16c99f06..f95dcc9c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -92,6 +92,9 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->sock >= 0) close(data->sock); + if (data->cfg.fd >= 0) + close(data->cfg.fd); + if (data->buffer) free(data->buffer); @@ -196,7 +199,7 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, proceed: buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; - if ((data->count + cfg.sample_size * size) <= cfg.pkt_len) + if ((data->count + size * frame_size) <= cfg.pkt_len) frames_to_write = size; else frames_to_write = (cfg.pkt_len - data->count) / frame_size; @@ -241,9 +244,9 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); - if ((data->count + frames_to_read * frame_size) != cfg.pkt_len) { - /* Remember we have some frame in the pipe now */ - data->count += frames_to_read * frame_size; + /* Remember we have some frame in the pipe now */ + data->count += frames_to_read * frame_size; + if (data->count != cfg.pkt_len) { ret = frames_to_read; goto done; } diff --git a/audio/unix.c b/audio/unix.c index f17404b4..a4b8f2c4 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -32,7 +32,6 @@ #include #include #include -#include #include -- cgit From 389d6a32fa14abefec394b9ebfa3b2deb51489c7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 27 Jun 2007 18:31:08 +0000 Subject: Revert changes on storage, rfcomm might change over time. --- audio/device.c | 2 +- audio/headset.c | 4 ++-- audio/headset.h | 2 +- audio/manager.c | 17 +++++------------ 4 files changed, 9 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 884a2661..785d0115 100644 --- a/audio/device.c +++ b/audio/device.c @@ -186,7 +186,7 @@ int device_store(struct device *device, gboolean is_default) if (is_default) textfile_put(filename, "default", dst_addr); if (device->headset) - snprintf(value, 64, "headset#%d", headset_get_channel(device)); + snprintf(value, 64, "headset"); else if (device->gateway) snprintf(value, 64, "gateway"); else if (device->sink) diff --git a/audio/headset.c b/audio/headset.c index 1e8b640a..2e285aa9 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1396,12 +1396,12 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) } struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc, int channel) + uint16_t svc) { struct headset *headset; headset = g_new0(struct headset, 1); - headset->rfcomm_ch = channel; + headset->rfcomm_ch = -1; headset->sp_gain = -1; headset->mic_gain = -1; diff --git a/audio/headset.h b/audio/headset.h index 8ef08249..c858d206 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -55,7 +55,7 @@ typedef enum { struct headset; struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc, int channel); + uint16_t svc); void headset_free(void *device); diff --git a/audio/manager.c b/audio/manager.c index 8ca57100..00f5d931 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -186,7 +186,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - record, uuid16, -1); + record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: debug("Found Headset AG record"); @@ -197,7 +197,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - record, uuid16, -1); + record, uuid16); break; case HANDSFREE_AGW_SVCLASS_ID: debug("Found Handsfree AG record"); @@ -637,7 +637,7 @@ struct device *manager_device_connected(bdaddr_t *bda) } if (!device->headset) - device->headset = headset_init(device, NULL, 0, -1); + device->headset = headset_init(device, NULL, 0); if (!device->headset) return NULL; @@ -1101,15 +1101,8 @@ static void parse_stored_devices(char *key, char *value, void *data) if (!device) return; - if (strncmp(value, "headset", strlen("headset")) == 0) { - int channel = -1; - char *ptr; - - if ((ptr = strchr(value, '#'))) - channel = strtol(ptr+1, NULL, 10); - - device->headset = headset_init(device, NULL, 0, channel); - } + if (strncmp(value, "headset", strlen("headset")) == 0) + device->headset = headset_init(device, NULL, 0); add_device(device); } -- cgit From b57f22a505f7c46662bfd5900df38dd68099df2c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 29 Jun 2007 19:49:47 +0000 Subject: Fix concurrent pending connections, properly checks state machine and lot of code cleanup. --- audio/headset.c | 411 +++++++++++++++++++++++++++----------------------------- 1 file changed, 195 insertions(+), 216 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 2e285aa9..28f6ac1d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -64,9 +64,10 @@ struct pending_connect { DBusMessage *msg; GIOChannel *io; + struct ipc_packet *pkt; guint io_id; int sock; - struct ipc_packet *pkt; + int err; }; struct headset { @@ -87,20 +88,22 @@ struct headset { headset_type_t type; headset_state_t state; - struct pending_connect *pending_connect; + GSList *pending; int sp_gain; int mic_gain; }; -static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, - void *data); static int rfcomm_connect(struct device *device, struct pending_connect *c); static void pending_connect_free(struct pending_connect *c) { - if (c->io) + if (c->pkt) + unix_send_cfg(c->sock, c->pkt); + if (c->io) { + g_io_channel_close(c->io); g_io_channel_unref(c->io); + } if (c->msg) dbus_message_unref(c->msg); g_free(c); @@ -174,13 +177,16 @@ static void close_sco(struct device *device) { struct headset *hs = device->headset; - g_io_channel_close(hs->sco); - g_io_channel_unref(hs->sco); - hs->sco = NULL; - hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, "Stopped", - DBUS_TYPE_INVALID); + if (hs->sco) { + g_io_channel_close(hs->sco); + g_io_channel_unref(hs->sco); + hs->sco = NULL; + hs->state = HEADSET_STATE_CONNECTED; + dbus_connection_emit_signal(device->conn, device->path, + AUDIO_HEADSET_INTERFACE, + "Stopped", + DBUS_TYPE_INVALID); + } } static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, @@ -280,9 +286,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: - if (hs->sco) - close_sco(device); - hs_disconnect(NULL, NULL, device); + close_sco(device); + headset_close_rfcomm(device); return FALSE; } @@ -299,8 +304,7 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, error("Audio connection got disconnected"); - if (hs->sco) - close_sco(device); + close_sco(device); return FALSE; } @@ -329,19 +333,41 @@ static GIOError headset_send(struct headset *hs, const char *str) return G_IO_ERROR_NONE; } +static void pending_connect_ok(struct pending_connect *c, struct device *dev) +{ + DBusMessage *reply; + + if (c->msg) { + reply = dbus_message_new_method_return(c->msg); + if (reply) + send_message_and_unref(dev->conn, reply); + } + else if (c->pkt) + headset_get_config(dev, c->sock, c->pkt); + pending_connect_free(c); +} + +static void pending_connect_failed(struct pending_connect *c, struct device *dev) +{ + if (c->msg) + err_connect_failed(dev->conn, c->msg, strerror(c->err)); + pending_connect_free(c); +} + static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *device) { struct headset *hs; + struct pending_connect *c; int ret, sk, err, flags; socklen_t len; - DBusMessage *reply; char str[13]; if (cond & G_IO_NVAL) return FALSE; hs = device->headset; + c = hs->pending->data; sk = g_io_channel_unix_get_fd(chan); @@ -362,27 +388,15 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, info("SCO fd=%d", sk); hs->sco = chan; - hs->pending_connect->io = NULL; + c->io = NULL; flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; g_io_add_watch(hs->sco, flags, (GIOFunc) sco_cb, device); - if (hs->pending_connect->msg) { - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(device->conn, reply); - } - else if (hs->pending_connect->pkt) { - headset_get_config(device, hs->pending_connect->sock, - hs->pending_connect->pkt); - unix_send_cfg(hs->pending_connect->sock, - hs->pending_connect->pkt); - } - - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - + g_slist_foreach(hs->pending, (GFunc)pending_connect_ok, device); + g_slist_free(hs->pending); + hs->pending = NULL; fcntl(sk, F_SETFL, 0); hs->state = HEADSET_STATE_PLAYING; @@ -403,16 +417,9 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - if (hs->pending_connect->msg) - err_connect_failed(device->conn, hs->pending_connect->msg, - strerror(err)); - if (hs->pending_connect->io) - g_io_channel_close(hs->pending_connect->io); - if (hs->pending_connect->pkt) - unix_send_cfg(hs->pending_connect->sock, - hs->pending_connect->pkt); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; + g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_free(hs->pending); + hs->pending = NULL; hs->state = HEADSET_STATE_CONNECTED; return FALSE; @@ -425,6 +432,12 @@ static int sco_connect(struct device *device, struct pending_connect *c) gboolean do_callback = FALSE; int sk, err; + if (!g_slist_find(hs->pending, c)) + hs->pending = g_slist_append(hs->pending, c); + + if (hs->state != HEADSET_STATE_CONNECTED) + return 0; + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { err = errno; @@ -474,7 +487,8 @@ static int sco_connect(struct device *device, struct pending_connect *c) } hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; - hs->pending_connect = c; + if (!g_slist_find(hs->pending, c)) + hs->pending = g_slist_append(hs->pending, c); if (do_callback) sco_connect_cb(c->io, G_IO_OUT, device); @@ -486,6 +500,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *device) { struct headset *hs; + struct pending_connect *c; char hs_address[18]; int sk, ret, err = 0; socklen_t len; @@ -494,6 +509,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; hs = device->headset; + c = hs->pending->data; sk = g_io_channel_unix_get_fd(chan); @@ -512,7 +528,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, ba2str(&device->dst, hs_address); hs->rfcomm = chan; - hs->pending_connect->io = NULL; + c->io = NULL; hs->state = HEADSET_STATE_CONNECTED; dbus_connection_emit_signal(device->conn, device->path, @@ -525,39 +541,26 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); - if (hs->pending_connect->msg) { - DBusMessage *reply; - - reply = dbus_message_new_method_return(hs->pending_connect->msg); - if (reply) - send_message_and_unref(device->conn, reply); - } - else if (hs->pending_connect->pkt) { - if (sco_connect(device, hs->pending_connect) < 0) + if (c->pkt) { + if (sco_connect(device, c) < 0) goto failed; return FALSE; } - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; + g_slist_foreach(hs->pending, (GFunc)pending_connect_ok, device); + g_slist_free(hs->pending); + hs->pending = NULL; return FALSE; failed: - if (hs->pending_connect->msg) - err_connect_failed(device->conn, hs->pending_connect->msg, - strerror(err)); - if (hs->pending_connect->pkt) - unix_send_cfg(hs->pending_connect->sock, - hs->pending_connect->pkt); - if (hs->pending_connect->io) - g_io_channel_close(hs->pending_connect->io); + g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_free(hs->pending); + hs->pending = NULL; if (hs->rfcomm) hs->state = HEADSET_STATE_CONNECTED; else hs->state = HEADSET_STATE_DISCONNECTED; - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; return FALSE; } @@ -575,7 +578,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct headset *hs = device->headset; struct pending_connect *c; - c = hs->pending_connect; + c = hs->pending->data; reply = dbus_pending_call_steal_reply(call); @@ -670,8 +673,9 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; + g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_free(hs->pending); + hs->pending = NULL; hs->state = HEADSET_STATE_DISCONNECTED; device_finish_sdp_transaction(device); } @@ -689,7 +693,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) dbus_uint32_t handle; int array_len; - c = hs->pending_connect; + c = hs->pending->data; reply = dbus_pending_call_steal_reply(call); @@ -771,7 +775,10 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - hs_disconnect(NULL, NULL, device); + g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_free(hs->pending); + hs->pending = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; } static int get_handles(struct device *device) @@ -824,15 +831,18 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) int sk, err; if (c != NULL) { - hs->pending_connect = c; - hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + if (!g_slist_find(hs->pending, c)) + hs->pending = g_slist_append(hs->pending, c); - if (hs->state != HEADSET_STATE_DISCONNECTED) - return -EBUSY; + hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; - if (hs->rfcomm_ch < 0) + if (hs->state == HEADSET_STATE_DISCONNECTED) return get_handles(device); + else + return 0; } + else + c = hs->pending->data; ba2str(&device->dst, address); @@ -866,8 +876,8 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) bacpy(&addr.rc_bdaddr, &device->dst); addr.rc_channel = hs->rfcomm_ch; - hs->pending_connect->io = g_io_channel_unix_new(sk); - if (!hs->pending_connect->io) { + c->io = g_io_channel_unix_new(sk); + if (!c->io) { err = ENOMEM; error("channel_unix_new failed in rfcomm connect"); goto failed; @@ -882,17 +892,17 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) debug("Connect in progress"); - g_io_add_watch(hs->pending_connect->io, G_IO_OUT | G_IO_NVAL, + g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, (GIOFunc) rfcomm_connect_cb, device); } else { debug("Connect succeeded with first try"); - rfcomm_connect_cb(hs->pending_connect->io, G_IO_OUT, device); + rfcomm_connect_cb(c->io, G_IO_OUT, device); } return 0; failed: - if (!hs->pending_connect->io && sk >= 0) + if (!c->io && sk >= 0) close(sk); return -err; @@ -905,30 +915,23 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, struct headset *hs = device->headset; DBusMessage *reply = NULL; - if (!hs || !hs->sco) - return err_not_connected(conn, msg); - - if (msg) { - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->state == HEADSET_STATE_PLAY_IN_PROGRESS && - hs->pending_connect) { - g_io_channel_close(hs->pending_connect->io); - if (hs->pending_connect->msg) - err_connect_failed(conn, hs->pending_connect->msg, - strerror(EINTR)); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; - hs->state = HEADSET_STATE_CONNECTED; + switch(hs->state) { + case HEADSET_STATE_PLAYING: + case HEADSET_STATE_PLAY_IN_PROGRESS: + close_sco(device); + break; + case HEADSET_STATE_CONNECTED: + case HEADSET_STATE_CONNECT_IN_PROGRESS: + case HEADSET_STATE_DISCONNECTED: + return err_not_connected(conn, msg); + break; } - close_sco(device); - - if (reply) - send_message_and_unref(conn, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -945,10 +948,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->state == HEADSET_STATE_PLAYING) - playing = TRUE; - else - playing = FALSE; + playing = (hs->state == HEADSET_STATE_PLAYING); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, DBUS_TYPE_INVALID); @@ -966,53 +966,27 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, DBusMessage *reply = NULL; char hs_address[18]; - if (msg) { - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - if (hs->state > HEADSET_STATE_CONNECTED) - hs_stop(NULL, NULL, device); - - if (hs->ring_timer) { - g_source_remove(hs->ring_timer); - hs->ring_timer = 0; - } - - if (hs->rfcomm) { - g_io_channel_close(hs->rfcomm); - g_io_channel_unref(hs->rfcomm); - hs->rfcomm = NULL; - } + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->pending_connect) { - if (hs->pending_connect->io) - g_io_channel_close(hs->pending_connect->io); - if (hs->pending_connect->io_id) - g_source_remove(hs->pending_connect->io_id); - if (hs->pending_connect->msg) - err_connect_failed(conn, - hs->pending_connect->msg, - strerror(EINTR)); - pending_connect_free(hs->pending_connect); - hs->pending_connect = NULL; + switch(hs->state) { + case HEADSET_STATE_PLAYING: + case HEADSET_STATE_PLAY_IN_PROGRESS: + close_sco(device); + case HEADSET_STATE_CONNECTED: + case HEADSET_STATE_CONNECT_IN_PROGRESS: + headset_close_rfcomm(device); + break; + case HEADSET_STATE_DISCONNECTED: + return err_not_connected(conn, msg); + break; } - hs->state = HEADSET_STATE_DISCONNECTED; - ba2str(&device->dst, hs_address); info("Disconnected from %s, %s", hs_address, device->path); - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, - "Disconnected", DBUS_TYPE_INVALID); - - hs->data_start = 0; - hs->data_length = 0; - - if (reply) - send_message_and_unref(conn, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1047,7 +1021,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, struct pending_connect *c; int err; - if (hs->state > HEADSET_STATE_DISCONNECTED || hs->pending_connect) + if (hs->state > HEADSET_STATE_DISCONNECTED) return err_already_connected(conn, msg); c = g_try_new0(struct pending_connect, 1); @@ -1063,8 +1037,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_HANDLED; error: - if (c) - pending_connect_free(c); + pending_connect_free(c); return err_connect_failed(conn, msg, strerror(-err)); } @@ -1088,11 +1061,9 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); - if (msg) { - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; if (hs->ring_timer) { debug("IndicateCall received when already indicating"); @@ -1107,8 +1078,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); done: - if (reply) - send_message_and_unref(conn, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1124,11 +1094,9 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); - if (msg) { - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; if (!hs->ring_timer) { debug("Got CancelRinging method call but ringing is not in progress"); @@ -1139,8 +1107,7 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, hs->ring_timer = 0; done: - if (reply) - send_message_and_unref(conn, reply); + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1156,10 +1123,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, if (hs->state < HEADSET_STATE_CONNECTED) return err_not_connected(conn, msg); - if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS || hs->pending_connect) - return err_already_connected(conn, msg); - - if (hs->sco) + if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS) return err_already_connected(conn, msg); c = g_try_new0(struct pending_connect, 1); @@ -1398,71 +1362,71 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) struct headset *headset_init(void *device, sdp_record_t *record, uint16_t svc) { - struct headset *headset; + struct device *dev = (struct device *) device; + struct headset *hs; - headset = g_new0(struct headset, 1); - headset->rfcomm_ch = -1; - headset->sp_gain = -1; - headset->mic_gain = -1; + hs = g_new0(struct headset, 1); + hs->rfcomm_ch = -1; + hs->sp_gain = -1; + hs->mic_gain = -1; if (!record) goto register_iface; switch (svc) { case HANDSFREE_SVCLASS_ID: - headset->hfp_handle = record->handle; + hs->hfp_handle = record->handle; break; case HEADSET_SVCLASS_ID: - headset->hsp_handle = record->handle; + hs->hsp_handle = record->handle; break; default: debug("Invalid record passed to headset_init"); - g_free(headset); + g_free(hs); return NULL; } - headset_set_channel(headset, record); + headset_set_channel(hs, record); register_iface: - if (!dbus_connection_register_interface(((struct device *) device)->conn, - ((struct device *) device)->path, + if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, headset_methods, headset_signals, NULL)) { - g_free(headset); + g_free(hs); return NULL; } - return headset; + return hs; } void headset_free(void *device) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - if (headset->sco) { - g_io_channel_close(headset->sco); - g_io_channel_unref(headset->sco); + if (hs->sco) { + g_io_channel_close(hs->sco); + g_io_channel_unref(hs->sco); } - if (headset->rfcomm) { - g_io_channel_close(headset->rfcomm); - g_io_channel_unref(headset->rfcomm); + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); } - g_free(headset); - headset = NULL; + g_free(hs); + hs = NULL; } int headset_get_config(void *device, int sock, struct ipc_packet *pkt) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; int err = EINVAL; struct pending_connect *c; - if (headset->rfcomm == NULL) { + if (hs->rfcomm == NULL) { c = g_try_new0(struct pending_connect, 1); if (c == NULL) goto error; @@ -1472,7 +1436,7 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) goto error; return 0; } - else if (headset->sco == NULL) { + else if (hs->sco == NULL) { c = g_try_new0(struct pending_connect, 1); if (c == NULL) goto error; @@ -1483,7 +1447,7 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) return 0; } - cfg->fd = g_io_channel_unix_get_fd(headset->sco); + cfg->fd = g_io_channel_unix_get_fd(hs->sco); cfg->fd_opt = CFG_FD_OPT_READWRITE; cfg->encoding = 0; cfg->bitpool = 0; @@ -1502,58 +1466,73 @@ error: headset_type_t headset_get_type(void *device) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - return headset->type; + return hs->type; } void headset_set_type(void *device, headset_type_t type) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - headset->type = type; + hs->type = type; } int headset_connect_rfcomm(void *device, int sock) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - headset->rfcomm = g_io_channel_unix_new(sock); + hs->rfcomm = g_io_channel_unix_new(sock); - return headset->rfcomm ? 0 : -EINVAL; + return hs->rfcomm ? 0 : -EINVAL; } int headset_close_rfcomm(void *device) { - struct headset *headset = ((struct device *) device)->headset; + struct device *dev = (struct device *) device; + struct headset *hs = dev->headset; - g_io_channel_close(headset->rfcomm); - g_io_channel_unref(headset->rfcomm); - headset->rfcomm = NULL; + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } + if (hs->rfcomm) { + g_io_channel_close(hs->rfcomm); + g_io_channel_unref(hs->rfcomm); + hs->rfcomm = NULL; + hs->state = HEADSET_STATE_DISCONNECTED; + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_HEADSET_INTERFACE, + "Disconnected", + DBUS_TYPE_INVALID); + } + + hs->data_start = 0; + hs->data_length = 0; return 0; } void headset_set_state(void *device, headset_state_t state) { - struct headset *headset = ((struct device *) device)->headset; + struct device *dev = (struct device *) device; + struct headset *hs = dev->headset; switch(state) { case HEADSET_STATE_DISCONNECTED: case HEADSET_STATE_CONNECT_IN_PROGRESS: break; case HEADSET_STATE_CONNECTED: - if (headset->rfcomm) { + if (hs->rfcomm) { char hs_address[18]; - g_io_add_watch(headset->rfcomm, + g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); ba2str(&((struct device *) device)->dst, hs_address); - dbus_connection_emit_signal(((struct device *) device)->conn, - ((struct device *) device)->path, + dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); @@ -1564,19 +1543,19 @@ void headset_set_state(void *device, headset_state_t state) break; } - headset->state = state; + hs->state = state; } headset_state_t headset_get_state(void *device) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - return headset->state; + return hs->state; } int headset_get_channel(void *device) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *hs = ((struct device *) device)->headset; - return headset->rfcomm_ch; + return hs->rfcomm_ch; } -- cgit From df235d1f395b10654024979180c778777bd50c71 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 29 Jun 2007 23:19:36 +0000 Subject: Handle state machine in a better way and add disconnect code for plugin. --- audio/headset.c | 124 +++++++++++++++++++++----------------------------- audio/pcm_bluetooth.c | 20 ++++++++ audio/unix.c | 54 ++++++++++++++++------ 3 files changed, 114 insertions(+), 84 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 28f6ac1d..ca24e93b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -181,11 +181,6 @@ static void close_sco(struct device *device) g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; - hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, - "Stopped", - DBUS_TYPE_INVALID); } } @@ -286,8 +281,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: - close_sco(device); - headset_close_rfcomm(device); + headset_set_state(device, HEADSET_STATE_DISCONNECTED); return FALSE; } @@ -304,7 +298,7 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, error("Audio connection got disconnected"); - close_sco(device); + headset_set_state(device, HEADSET_STATE_CONNECTED); return FALSE; } @@ -359,9 +353,8 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, { struct headset *hs; struct pending_connect *c; - int ret, sk, err, flags; + int ret, sk, err; socklen_t len; - char str[13]; if (cond & G_IO_NVAL) return FALSE; @@ -390,29 +383,12 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs->sco = chan; c->io = NULL; - flags = G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(hs->sco, flags, (GIOFunc) sco_cb, device); - g_slist_foreach(hs->pending, (GFunc)pending_connect_ok, device); g_slist_free(hs->pending); hs->pending = NULL; fcntl(sk, F_SETFL, 0); - hs->state = HEADSET_STATE_PLAYING; - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, - "Playing", DBUS_TYPE_INVALID); - - if (hs->sp_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", hs->sp_gain); - headset_send(device->headset, str); - } - - if (hs->mic_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", hs->sp_gain); - headset_send(device->headset, str); - } + headset_set_state(device, HEADSET_STATE_PLAYING); return FALSE; @@ -420,7 +396,7 @@ failed: g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; - hs->state = HEADSET_STATE_CONNECTED; + headset_set_state(device, HEADSET_STATE_CONNECTED); return FALSE; } @@ -486,7 +462,7 @@ static int sco_connect(struct device *device, struct pending_connect *c) do_callback = TRUE; } - hs->state = HEADSET_STATE_PLAY_IN_PROGRESS; + headset_set_state(device, HEADSET_STATE_PLAY_IN_PROGRESS); if (!g_slist_find(hs->pending, c)) hs->pending = g_slist_append(hs->pending, c); @@ -530,12 +506,8 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, hs->rfcomm = chan; c->io = NULL; - hs->state = HEADSET_STATE_CONNECTED; - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, - "Connected", DBUS_TYPE_INVALID); + headset_set_state(device, HEADSET_STATE_CONNECTED); - device_store(device, FALSE); debug("Connected to %s", hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, @@ -558,9 +530,9 @@ failed: g_slist_free(hs->pending); hs->pending = NULL; if (hs->rfcomm) - hs->state = HEADSET_STATE_CONNECTED; + headset_set_state(device, HEADSET_STATE_CONNECTED); else - hs->state = HEADSET_STATE_DISCONNECTED; + headset_set_state(device, HEADSET_STATE_DISCONNECTED); return FALSE; } @@ -676,7 +648,7 @@ failed: g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; + headset_set_state(device, HEADSET_STATE_DISCONNECTED); device_finish_sdp_transaction(device); } @@ -778,7 +750,7 @@ failed: g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; + headset_set_state(device, HEADSET_STATE_DISCONNECTED); } static int get_handles(struct device *device) @@ -809,7 +781,7 @@ static int get_handles(struct device *device) DBUS_TYPE_STRING, &hs_svc, DBUS_TYPE_INVALID); - hs->state = HEADSET_STATE_CONNECT_IN_PROGRESS; + headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceHandles failed"); dbus_message_unref(msg); @@ -919,18 +891,10 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - switch(hs->state) { - case HEADSET_STATE_PLAYING: - case HEADSET_STATE_PLAY_IN_PROGRESS: - close_sco(device); - break; - case HEADSET_STATE_CONNECTED: - case HEADSET_STATE_CONNECT_IN_PROGRESS: - case HEADSET_STATE_DISCONNECTED: + if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) return err_not_connected(conn, msg); - break; - } + headset_set_state(device, HEADSET_STATE_CONNECTED); send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; @@ -970,19 +934,10 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - switch(hs->state) { - case HEADSET_STATE_PLAYING: - case HEADSET_STATE_PLAY_IN_PROGRESS: - close_sco(device); - case HEADSET_STATE_CONNECTED: - case HEADSET_STATE_CONNECT_IN_PROGRESS: - headset_close_rfcomm(device); - break; - case HEADSET_STATE_DISCONNECTED: + if (hs->state == HEADSET_STATE_DISCONNECTED) return err_not_connected(conn, msg); - break; - } + headset_set_state(device, HEADSET_STATE_DISCONNECTED); ba2str(&device->dst, hs_address); info("Disconnected from %s, %s", hs_address, device->path); @@ -1500,11 +1455,6 @@ int headset_close_rfcomm(void *device) g_io_channel_close(hs->rfcomm); g_io_channel_unref(hs->rfcomm); hs->rfcomm = NULL; - hs->state = HEADSET_STATE_DISCONNECTED; - dbus_connection_emit_signal(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Disconnected", - DBUS_TYPE_INVALID); } hs->data_start = 0; @@ -1517,32 +1467,64 @@ void headset_set_state(void *device, headset_state_t state) { struct device *dev = (struct device *) device; struct headset *hs = dev->headset; + char str[13]; + + if (hs->state == state) + return; switch(state) { case HEADSET_STATE_DISCONNECTED: + close_sco(device); + headset_close_rfcomm(device); + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_HEADSET_INTERFACE, + "Disconnected", + DBUS_TYPE_INVALID); + break; case HEADSET_STATE_CONNECT_IN_PROGRESS: break; case HEADSET_STATE_CONNECTED: - if (hs->rfcomm) { - char hs_address[18]; - + if (hs->state < state) { g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); - ba2str(&((struct device *) device)->dst, hs_address); - dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); } + else { + close_sco(device); + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_HEADSET_INTERFACE, + "Stopped", + DBUS_TYPE_INVALID); + } break; case HEADSET_STATE_PLAY_IN_PROGRESS: + break; case HEADSET_STATE_PLAYING: + g_io_add_watch(hs->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) sco_cb, device); + + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_HEADSET_INTERFACE, + "Playing", DBUS_TYPE_INVALID); + + if (hs->sp_gain >= 0) { + snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", hs->sp_gain); + headset_send(hs, str); + } + + if (hs->mic_gain >= 0) { + snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", hs->sp_gain); + headset_send(hs, str); + } break; } + debug("State changed %s: %d -> %d", dev->path, hs->state, state); hs->state = state; } diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index f95dcc9c..71daab4f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -86,6 +86,26 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) static void bluetooth_exit(struct bluetooth_data *data) { + int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_status); + struct ipc_packet *pkt; + struct ipc_data_status *status; + + DBG("Sending PKT_TYPE_STATUS_REQ..."); + + if ((pkt = malloc(len)) == 0) + goto done; + + memset(pkt, 0, len); + pkt->type = PKT_TYPE_STATUS_REQ; + pkt->role = PKT_ROLE_NONE; + pkt->error = PKT_ERROR_NONE; + + status = (struct ipc_data_status *) pkt->data; + status->status = STATUS_DISCONNECTED; + + if ((ret = send(data->sock, pkt, len, 0)) < 0) + DBG("OK"); +done: if (data == NULL) return; diff --git a/audio/unix.c b/audio/unix.c index a4b8f2c4..0f6c53aa 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -75,13 +75,49 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) return sendmsg(sock, &msgh, MSG_NOSIGNAL); } -static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) +static void cfg_event(int clisk, struct ipc_packet *pkt) +{ + struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; + struct device *device; + + memset(cfg, 0, sizeof(struct ipc_data_cfg)); + + if ((device = manager_default_device())) { + if (device->headset) + headset_get_config(device, clisk, pkt); + } + else + cfg->fd = -1; + + if (cfg->fd != 0) + unix_send_cfg(clisk, pkt); +} + +static void ctl_event(int clisk, struct ipc_packet *pkt) +{ +} + +static void status_event(int clisk, struct ipc_packet *pkt) { + struct ipc_data_status *status = (struct ipc_data_status *) pkt->data; struct device *device; + + if (status->status == STATUS_DISCONNECTED) { + if (!(device = manager_default_device())) + return; + + if (device->headset) + headset_set_state(device, HEADSET_STATE_DISCONNECTED); + } + + g_free(pkt); +} + +static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) +{ struct sockaddr_un addr; socklen_t addrlen; struct ipc_packet *pkt; - struct ipc_data_cfg *cfg; int sk, clisk, len; debug("chan %p cond %td data %p", chan, cond, data); @@ -114,23 +150,15 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) switch (pkt->type) { case PKT_TYPE_CFG_REQ: info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); - - cfg = (struct ipc_data_cfg *) pkt->data; - - memset(cfg, 0, sizeof(struct ipc_data_cfg)); - if ((device = manager_default_device())) { - if (device->headset) - headset_get_config(device, clisk, pkt); - } - - if (cfg->fd != 0) - unix_send_cfg(clisk, pkt); + cfg_event(clisk, pkt); break; case PKT_TYPE_STATUS_REQ: info("Package PKT_TYPE_STATUS_REQ"); + status_event(clisk, pkt); break; case PKT_TYPE_CTL_REQ: info("Package PKT_TYPE_CTL_REQ"); + ctl_event(clisk, pkt); break; } -- cgit From 66d48b0ac25c85de354f26782a377d0ad91bd322 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 10 Jul 2007 13:34:57 +0000 Subject: Code cleanup. --- audio/gateway.c | 4 +++- audio/headset.c | 2 +- audio/headset.h | 10 +++++----- audio/ipc.h | 17 +++++++++-------- audio/pcm_bluetooth.c | 16 +++++++++------- audio/unix.c | 45 +++++++++++++++++++++++++++++++++------------ audio/unix.h | 1 + 7 files changed, 61 insertions(+), 34 deletions(-) (limited to 'audio') diff --git a/audio/gateway.c b/audio/gateway.c index afdcbe01..3b84462d 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -314,7 +314,8 @@ static void auth_cb(DBusPendingCall *call, void *data) send_cancel_auth(device); } dbus_error_free(&err); - headset_close_rfcomm(device); + + headset_set_state(device, HEADSET_STATE_DISCONNECTED); } else { char hs_address[18]; @@ -407,6 +408,7 @@ static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, void *data) dbus_pending_call_set_notify(pending, auth_cb, device, NULL); dbus_pending_call_unref(pending); dbus_message_unref(auth); + headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); return TRUE; diff --git a/audio/headset.c b/audio/headset.c index ca24e93b..edb323ee 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -206,7 +206,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read); if (err != G_IO_ERROR_NONE) - goto failed; + error("Ignoring error %d", err); free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; diff --git a/audio/headset.h b/audio/headset.h index c858d206..d3fd86d9 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -40,11 +40,11 @@ typedef enum { } headset_event_t; typedef enum { - HEADSET_STATE_DISCONNECTED = 0, - HEADSET_STATE_CONNECT_IN_PROGRESS, - HEADSET_STATE_CONNECTED, - HEADSET_STATE_PLAY_IN_PROGRESS, - HEADSET_STATE_PLAYING, + HEADSET_STATE_DISCONNECTED = STATE_DISCONNECTED, + HEADSET_STATE_CONNECT_IN_PROGRESS = STATE_CONNECTING, + HEADSET_STATE_CONNECTED = STATE_CONNECTED, + HEADSET_STATE_PLAY_IN_PROGRESS = STATE_STREAM_STARTING, + HEADSET_STATE_PLAYING = STATE_STREAMING, } headset_state_t; typedef enum { diff --git a/audio/ipc.h b/audio/ipc.h index d340d15e..e56dca24 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -40,8 +40,8 @@ /* Packet types */ #define PKT_TYPE_CFG_REQ 0 #define PKT_TYPE_CFG_RSP 1 -#define PKT_TYPE_STATUS_REQ 2 -#define PKT_TYPE_STATUS_RSP 3 +#define PKT_TYPE_STATE_REQ 2 +#define PKT_TYPE_STATE_RSP 3 #define PKT_TYPE_CTL_REQ 4 #define PKT_TYPE_CTL_RSP 5 #define PKT_TYPE_CTL_NTFY 6 @@ -75,13 +75,14 @@ struct ipc_data_cfg { } __attribute__ ((packed)); /* Device status */ -#define STATUS_DISCONNECTED 0 -#define STATUS_CONNECTING 1 -#define STATUS_CONNECTED 2 -#define STATUS_STREAMING 3 +#define STATE_DISCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_CONNECTED 2 +#define STATE_STREAM_STARTING 3 +#define STATE_STREAMING 4 -struct ipc_data_status { - uint8_t status; /* Stream status */ +struct ipc_data_state { + uint8_t state; /* Stream state */ } __attribute__ ((packed)); #define CTL_MODE_PLAYBACK 0 diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 71daab4f..92d0383c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -86,25 +86,27 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) static void bluetooth_exit(struct bluetooth_data *data) { - int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_status); + int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); struct ipc_packet *pkt; - struct ipc_data_status *status; + struct ipc_data_state *state; DBG("Sending PKT_TYPE_STATUS_REQ..."); - if ((pkt = malloc(len)) == 0) + if ((pkt = malloc(len)) == NULL) goto done; memset(pkt, 0, len); - pkt->type = PKT_TYPE_STATUS_REQ; + pkt->type = PKT_TYPE_STATE_REQ; pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; - status = (struct ipc_data_status *) pkt->data; - status->status = STATUS_DISCONNECTED; + state = (struct ipc_data_state *) pkt->data; + state->state = STATE_DISCONNECTED; if ((ret = send(data->sock, pkt, len, 0)) < 0) - DBG("OK"); + DBG("Error %s (%d)", strerror(errno), errno); + + free(pkt); done: if (data == NULL) return; diff --git a/audio/unix.c b/audio/unix.c index 0f6c53aa..fbda7ed9 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -97,18 +97,18 @@ static void ctl_event(int clisk, struct ipc_packet *pkt) { } -static void status_event(int clisk, struct ipc_packet *pkt) +static void state_event(int clisk, struct ipc_packet *pkt) { - struct ipc_data_status *status = (struct ipc_data_status *) pkt->data; + struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; struct device *device; - if (status->status == STATUS_DISCONNECTED) { - if (!(device = manager_default_device())) - return; + if (!(device = manager_default_device())) + return; - if (device->headset) - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - } + if (device->headset) + headset_set_state(device, state->state); + + unix_send_status(clisk, pkt); g_free(pkt); } @@ -152,9 +152,9 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); cfg_event(clisk, pkt); break; - case PKT_TYPE_STATUS_REQ: - info("Package PKT_TYPE_STATUS_REQ"); - status_event(clisk, pkt); + case PKT_TYPE_STATE_REQ: + info("Package PKT_TYPE_STATE_REQ"); + state_event(clisk, pkt); break; case PKT_TYPE_CTL_REQ: info("Package PKT_TYPE_CTL_REQ"); @@ -239,6 +239,27 @@ int unix_send_cfg(int sock, struct ipc_packet *pkt) } g_free(pkt); - close(sock); + return 0; +} + +int unix_send_status(int sock, struct ipc_packet *pkt) +{ + struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; + int len; + + info("status=%u", state->state); + + pkt->type = PKT_TYPE_CFG_RSP; + pkt->length = sizeof(struct ipc_data_state); + pkt->error = PKT_ERROR_NONE; + + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); + len = send(sock, pkt, len, 0); + if (len < 0) + info("Error %s(%d)", strerror(errno), errno); + + info("%d bytes sent", len); + + g_free(pkt); return 0; } diff --git a/audio/unix.h b/audio/unix.h index 15da23ab..32cf4af9 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -27,3 +27,4 @@ int unix_init(void); void unix_exit(void); int unix_send_cfg(int sock, struct ipc_packet *pkt); +int unix_send_status(int sock, struct ipc_packet *pkt); -- cgit From a96cee3d98d347664998c718a47082492bb9af3d Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 10 Jul 2007 18:10:35 +0000 Subject: Fix loading default device from every adapter present and devices that are was previous loaded. --- audio/manager.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 00f5d931..23811ae2 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1094,8 +1094,13 @@ static void parse_stored_devices(char *key, char *value, void *data) if (!key || !value || strcmp(key, "default") == 0) return; - info("Loading device %s (%s)", key, value); str2ba(key, &dst); + device = find_device(&dst); + + if (device) + return; + + info("Loading device %s (%s)", key, value); device = create_device(&dst); if (!device) @@ -1112,9 +1117,11 @@ static void register_devices_stored(const char *adapter) char filename[PATH_MAX + 1]; struct stat st; struct device *device; + bdaddr_t default_src; bdaddr_t src; bdaddr_t dst; char *addr; + int dev_id; create_name(filename, PATH_MAX, STORAGEDIR, adapter, "audio"); @@ -1128,6 +1135,14 @@ static void register_devices_stored(const char *adapter) textfile_foreach(filename, parse_stored_devices, &src); + bacpy(&default_src, BDADDR_ANY); + dev_id = hci_get_route(NULL); + if (dev_id < 0) + hci_devba(dev_id, &default_src); + + if (bacmp(&default_src, &src) != 0) + return; + addr = textfile_get(filename, "default"); if (!addr) return; -- cgit From 7c386bc929463c6aad12677bfe0de8238ce26220 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 18 Jul 2007 18:25:13 +0000 Subject: Fix disable_hfp and add human readable string for states. --- audio/gateway.c | 19 +++++++++++++++++++ audio/gateway.h | 2 ++ audio/headset.c | 36 ++++++++++++++++++++++-------------- 3 files changed, 43 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/gateway.c b/audio/gateway.c index 3b84462d..0c540de5 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -551,3 +551,22 @@ void gateway_exit(void) dbus_connection_unref(connection); connection = NULL; } + +gboolean gateway_is_enabled(uint16_t svc) +{ + gboolean ret; + + switch (svc) { + case HEADSET_SVCLASS_ID: + ret = (hs_server != NULL); + break; + case HANDSFREE_SVCLASS_ID: + ret = (hf_server != NULL); + break; + default: + ret = FALSE; + break; + } + + return ret; +} diff --git a/audio/gateway.h b/audio/gateway.h index 137187cb..8794ac99 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -28,3 +28,5 @@ struct gateway; int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); void gateway_exit(void); + +gboolean gateway_is_enabled(uint16_t svc); diff --git a/audio/headset.c b/audio/headset.c index edb323ee..38127a90 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -61,6 +61,9 @@ #define HEADSET_GAIN_SPEAKER 'S' #define HEADSET_GAIN_MICROPHONE 'M' +static char *str_state[] = {"DISCONNECTED", "CONNECTING", "CONNECTED", + "STREAM_STARTING", "STREAMING"}; + struct pending_connect { DBusMessage *msg; GIOChannel *io; @@ -453,14 +456,11 @@ static int sco_connect(struct device *device, struct pending_connect *c) return -err; } - debug("SCO connect in progress"); c->io_id = g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, (GIOFunc) sco_connect_cb, device); - } else { - debug("SCO connect succeeded with first try"); + } else do_callback = TRUE; - } headset_set_state(device, HEADSET_STATE_PLAY_IN_PROGRESS); if (!g_slist_find(hs->pending, c)) @@ -508,7 +508,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, headset_set_state(device, HEADSET_STATE_CONNECTED); - debug("Connected to %s", hs_address); + debug("%s: Connected to %s", device->path, hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); @@ -818,7 +818,8 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) ba2str(&device->dst, address); - debug("Connecting to %s channel %d", address, hs->rfcomm_ch); + debug("%s: Connecting to %s channel %d", device->path, address, + hs->rfcomm_ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { @@ -862,14 +863,10 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) goto failed; } - debug("Connect in progress"); - g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, (GIOFunc) rfcomm_connect_cb, device); - } else { - debug("Connect succeeded with first try"); + } else rfcomm_connect_cb(c->io, G_IO_OUT, device); - } return 0; @@ -1280,6 +1277,9 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) { struct headset *headset = ((struct device *) device)->headset; + if (!gateway_is_enabled(svc)) + return; + switch (svc) { case HANDSFREE_SVCLASS_ID: if (headset->hfp_handle && @@ -1328,6 +1328,11 @@ struct headset *headset_init(void *device, sdp_record_t *record, if (!record) goto register_iface; + if (!gateway_is_enabled(svc)) { + g_free(hs); + return NULL; + } + switch (svc) { case HANDSFREE_SVCLASS_ID: hs->hfp_handle = record->handle; @@ -1513,18 +1518,21 @@ void headset_set_state(void *device, headset_state_t state) "Playing", DBUS_TYPE_INVALID); if (hs->sp_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", hs->sp_gain); + snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", + hs->sp_gain); headset_send(hs, str); } if (hs->mic_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", hs->sp_gain); + snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", + hs->sp_gain); headset_send(hs, str); } break; } - debug("State changed %s: %d -> %d", dev->path, hs->state, state); + debug("State changed %s: %s -> %s", dev->path, str_state[hs->state], + str_state[state]); hs->state = state; } -- cgit From 06455ab12fe0df1278899ce31ae7a771495186ea Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 19 Jul 2007 11:28:42 +0000 Subject: Minor coding style fixes --- audio/headset.c | 12 ++++++------ audio/manager.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 38127a90..a95d5f46 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -386,7 +386,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, hs->sco = chan; c->io = NULL; - g_slist_foreach(hs->pending, (GFunc)pending_connect_ok, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device); g_slist_free(hs->pending); hs->pending = NULL; fcntl(sk, F_SETFL, 0); @@ -396,7 +396,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; headset_set_state(device, HEADSET_STATE_CONNECTED); @@ -519,14 +519,14 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } - g_slist_foreach(hs->pending, (GFunc)pending_connect_ok, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device); g_slist_free(hs->pending); hs->pending = NULL; return FALSE; failed: - g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; if (hs->rfcomm) @@ -645,7 +645,7 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); - g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; headset_set_state(device, HEADSET_STATE_DISCONNECTED); @@ -747,7 +747,7 @@ failed: if (msg) dbus_message_unref(msg); dbus_message_unref(reply); - g_slist_foreach(hs->pending, (GFunc)pending_connect_failed, device); + g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; headset_set_state(device, HEADSET_STATE_DISCONNECTED); diff --git a/audio/manager.c b/audio/manager.c index 23811ae2..b84d684b 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1186,7 +1186,7 @@ static void manager_unregister(DBusConnection *conn, void *data) info("Unregistered manager path"); if (devices) { - g_slist_foreach(devices, (GFunc)remove_device, NULL); + g_slist_foreach(devices, (GFunc) remove_device, NULL); g_slist_free(devices); devices = NULL; } -- cgit From 788c58f5a9c0a9439efc44c68f7ed3f5460eefed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 19 Jul 2007 11:45:49 +0000 Subject: Don't try to process data if g_io_channel_read failed --- audio/headset.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a95d5f46..1e57c177 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -208,8 +208,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read); - if (err != G_IO_ERROR_NONE) + if (err != G_IO_ERROR_NONE) { error("Ignoring error %d", err); + return TRUE; + } free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; -- cgit From ac9481b7ad48944f727f6f42237e4e4da32a10a7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 20 Jul 2007 18:03:20 +0000 Subject: Move server registration and sdp record code from gateway to manager. --- audio/gateway.c | 546 -------------------------------------------------------- audio/headset.c | 8 - audio/main.c | 9 +- audio/manager.c | 539 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/manager.h | 2 +- 5 files changed, 534 insertions(+), 570 deletions(-) (limited to 'audio') diff --git a/audio/gateway.c b/audio/gateway.c index 0c540de5..2f4ab59e 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -24,549 +24,3 @@ #ifdef HAVE_CONFIG_H #include #endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include "dbus.h" -#include "dbus-helper.h" -#include "logging.h" -#include "manager.h" -#include "error.h" - -static uint32_t hs_record_id = 0; -static uint32_t hf_record_id = 0; - -static GIOChannel *hs_server = NULL; -static GIOChannel *hf_server = NULL; - -static DBusConnection *connection = NULL; - -static int gateway_hsp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - sdp_data_t *channel; - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); - profile.version = 0x0100; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 gateway_hfp_ag_record(sdp_buf_t *buf, uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t record; - uint16_t u16 = 0x0009; - sdp_data_t *channel, *features; - uint8_t netid = 0x01; - sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); - int ret; - - 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); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - features = sdp_data_alloc(SDP_UINT16, &u16); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); - - sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); - - sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; - - sdp_data_free(channel); - 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 uint32_t gateway_add_ag_record(uint8_t channel, sdp_buf_t *buf) -{ - DBusMessage *msg, *reply; - DBusError derr; - dbus_uint32_t rec_id; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "AddServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf->data, buf->data_size, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, - msg, -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("Adding service record failed: %s", derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", - derr.message); - dbus_message_unref(reply); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - debug("add_ag_record: got record id 0x%x", rec_id); - - return rec_id; -} - -static int gateway_remove_ag_record(uint32_t rec_id) -{ - DBusMessage *msg, *reply; - DBusError derr; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RemoveServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, - msg, -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", - rec_id, derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - return 0; -} - -static void send_cancel_auth(struct device *device) -{ - DBusMessage *cancel; - char addr[18], *address = addr; - const char *uuid; - - if (headset_get_type(device) == SVC_HEADSET) - uuid = HSP_AG_UUID; - else - uuid = HFP_AG_UUID; - - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(&device->dst, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - send_message_and_unref(connection, cancel); -} - -static void auth_cb(DBusPendingCall *call, void *data) -{ - struct device *device = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - 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"); - send_cancel_auth(device); - } - dbus_error_free(&err); - - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - } else { - char hs_address[18]; - - headset_set_state(device, HEADSET_STATE_CONNECTED); - - ba2str(&device->dst, hs_address); - - debug("Accepted headset connection from %s for %s", - hs_address, device->path); - } - - dbus_message_unref(reply); -} - -static gboolean gateway_io_cb(GIOChannel *chan, GIOCondition cond, void *data) -{ - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; - char hs_address[18], *address = hs_address; - const char *uuid; - struct device *device; - DBusMessage *auth; - DBusPendingCall *pending; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - device = manager_device_connected(&addr.rc_bdaddr); - if (!device) { - close(cli_sk); - return TRUE; - } - - if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { - debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; - } - - if (headset_connect_rfcomm(device, cli_sk) < 0) { - error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; - } - - if (chan == hs_server) { - headset_set_type(device, SVC_HEADSET); - uuid = HSP_AG_UUID; - } else { - headset_set_type(device, SVC_HANDSFREE); - uuid = HFP_AG_UUID; - } - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); - goto failed; - } - - ba2str(&device->dst, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { - error("Sending of authorization request failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, auth_cb, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); - headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); - - return TRUE; - -failed: - headset_close_rfcomm(device); - - return TRUE; -} - -static GIOChannel *server_socket(uint8_t *channel) -{ - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = RFCOMM_LM_SECURE; - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = channel ? *channel : 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; -} - -int gateway_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) -{ - uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_buf_t buf; - - connection = dbus_connection_ref(conn); - - hs_server = server_socket(&chan); - if (!hs_server) - return -1; - - if (gateway_hsp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - hs_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hs_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hs_server); - hs_server = NULL; - return -1; - } - - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); - - if (no_hfp) - return 0; - - chan = DEFAULT_HF_AG_CHANNEL; - - hf_server = server_socket(&chan); - if (!hf_server) - return -1; - - if (gateway_hfp_ag_record(&buf, chan) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - hf_record_id = gateway_add_ag_record(chan, &buf); - free(buf.data); - if (!hf_record_id) { - error("Unable to register HS AG service record"); - g_io_channel_unref(hf_server); - hs_server = NULL; - return -1; - } - - g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) gateway_io_cb, NULL); - - return 0; -} - -void gateway_exit(void) -{ - if (hs_record_id) { - gateway_remove_ag_record(hs_record_id); - hs_record_id = 0; - } - - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; - } - - if (hf_record_id) { - gateway_remove_ag_record(hf_record_id); - hf_record_id = 0; - } - - if (hf_server) { - g_io_channel_unref(hf_server); - hf_server = NULL; - } - - dbus_connection_unref(connection); - connection = NULL; -} - -gboolean gateway_is_enabled(uint16_t svc) -{ - gboolean ret; - - switch (svc) { - case HEADSET_SVCLASS_ID: - ret = (hs_server != NULL); - break; - case HANDSFREE_SVCLASS_ID: - ret = (hf_server != NULL); - break; - default: - ret = FALSE; - break; - } - - return ret; -} diff --git a/audio/headset.c b/audio/headset.c index 1e57c177..e97a99cf 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1279,9 +1279,6 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) { struct headset *headset = ((struct device *) device)->headset; - if (!gateway_is_enabled(svc)) - return; - switch (svc) { case HANDSFREE_SVCLASS_ID: if (headset->hfp_handle && @@ -1330,11 +1327,6 @@ struct headset *headset_init(void *device, sdp_record_t *record, if (!record) goto register_iface; - if (!gateway_is_enabled(svc)) { - g_free(hs); - return NULL; - } - switch (svc) { case HANDSFREE_SVCLASS_ID: hs->hfp_handle = record->handle; diff --git a/audio/main.c b/audio/main.c index a7c93201..4da85a29 100644 --- a/audio/main.c +++ b/audio/main.c @@ -131,16 +131,11 @@ int main(int argc, char *argv[]) exit(1); } - if (audio_init(conn) < 0) { + if (audio_init(conn, disable_hfp, sco_hci) < 0) { error("Audio init failed!"); exit(1); } - if (gateway_init(conn, disable_hfp, sco_hci) < 0) { - error("Headset initialization failed!"); - exit(1); - } - if (argc > 1 && !strcmp(argv[1], "-s")) register_external_service(conn, "audio", "Audio service", ""); @@ -148,8 +143,6 @@ int main(int argc, char *argv[]) audio_exit(); - gateway_exit(); - unix_exit(); dbus_connection_unref(conn); diff --git a/audio/manager.c b/audio/manager.c index b84d684b..7e250785 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,12 @@ static struct device *default_dev = NULL; static GSList *devices = NULL; +static uint32_t hs_record_id = 0; +static uint32_t hf_record_id = 0; + +static GIOChannel *hs_server = NULL; +static GIOChannel *hf_server = NULL; + static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, struct audio_sdp_data *data); @@ -172,6 +179,25 @@ done: return uuid16; } +static gboolean server_is_enabled(uint16_t svc) +{ + gboolean ret; + + switch (svc) { + case HEADSET_SVCLASS_ID: + ret = (hs_server != NULL); + break; + case HANDSFREE_SVCLASS_ID: + ret = (hf_server != NULL); + break; + default: + ret = FALSE; + break; + } + + return ret; +} + static void handle_record(sdp_record_t *record, struct device *device) { gboolean is_default; @@ -179,6 +205,9 @@ static void handle_record(sdp_record_t *record, struct device *device) uuid16 = get_service_uuid(record); + if (!server_is_enabled(uuid16)) + return; + switch (uuid16) { case HEADSET_SVCLASS_ID: debug("Found Headset record"); @@ -1192,12 +1221,507 @@ static void manager_unregister(DBusConnection *conn, void *data) } } -int audio_init(DBusConnection *conn) +static int hsp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + sdp_data_t *channel; + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t record; + uint16_t u16 = 0x0009; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); + int ret; + + 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); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0105; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(&record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + features = sdp_data_alloc(SDP_UINT16, &u16); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(&record, aproto); + + sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + + sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); + + if (sdp_gen_record_pdu(&record, buf) < 0) + ret = -1; + else + ret = 0; + + sdp_data_free(channel); + 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 uint32_t add_record(uint8_t channel, sdp_buf_t *buf) +{ + DBusMessage *msg, *reply; + DBusError derr; + dbus_uint32_t rec_id; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "AddServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &buf->data, buf->data_size, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, + msg, -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("Adding service record failed: %s", derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Invalid arguments to AddServiceRecord reply: %s", + derr.message); + dbus_message_unref(reply); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + debug("add_record: got record id 0x%x", rec_id); + + return rec_id; +} + +static int remove_record(uint32_t rec_id) +{ + DBusMessage *msg, *reply; + DBusError derr; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RemoveServiceRecord"); + if (!msg) { + error("Can't allocate new method call"); + return 0; + } + + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(connection, + msg, -1, &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr)) { + error("Removing service record 0x%x failed: %s", + rec_id, derr.message); + dbus_error_free(&derr); + return 0; + } + + dbus_message_unref(reply); + + return 0; +} + +static void send_cancel_auth(struct device *device) +{ + DBusMessage *cancel; + char addr[18], *address = addr; + const char *uuid; + + if (headset_get_type(device) == SVC_HEADSET) + uuid = HSP_AG_UUID; + else + uuid = HFP_AG_UUID; + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(&device->dst, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); +} + +static void auth_cb(DBusPendingCall *call, void *data) +{ + struct device *device = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + 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"); + send_cancel_auth(device); + } + dbus_error_free(&err); + + headset_set_state(device, HEADSET_STATE_DISCONNECTED); + } else { + char hs_address[18]; + + headset_set_state(device, HEADSET_STATE_CONNECTED); + + ba2str(&device->dst, hs_address); + + debug("Accepted headset connection from %s for %s", + hs_address, device->path); + } + + dbus_message_unref(reply); +} + +static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +{ + int srv_sk, cli_sk; + struct sockaddr_rc addr; + socklen_t size; + char hs_address[18], *address = hs_address; + const char *uuid; + struct device *device; + DBusMessage *auth; + DBusPendingCall *pending; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on rfcomm server socket"); + g_io_channel_close(chan); + raise(SIGTERM); + return FALSE; + } + + srv_sk = g_io_channel_unix_get_fd(chan); + + size = sizeof(struct sockaddr_rc); + cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); + if (cli_sk < 0) { + error("accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + device = manager_device_connected(&addr.rc_bdaddr); + if (!device) { + close(cli_sk); + return TRUE; + } + + if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { + debug("Refusing new connection since one already exists"); + close(cli_sk); + return TRUE; + } + + if (headset_connect_rfcomm(device, cli_sk) < 0) { + error("Allocating new GIOChannel failed!"); + close(cli_sk); + return TRUE; + } + + if (chan == hs_server) { + headset_set_type(device, SVC_HEADSET); + uuid = HSP_AG_UUID; + } else { + headset_set_type(device, SVC_HANDSFREE); + uuid = HFP_AG_UUID; + } + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocate RequestAuthorization method call"); + goto failed; + } + + ba2str(&device->dst, hs_address); + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { + error("Sending of authorization request failed"); + goto failed; + } + + dbus_pending_call_set_notify(pending, auth_cb, device, NULL); + dbus_pending_call_unref(pending); + dbus_message_unref(auth); + headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); + + return TRUE; + +failed: + headset_close_rfcomm(device); + + return TRUE; +} + +static GIOChannel *server_socket(uint8_t *channel) +{ + int sock, lm; + struct sockaddr_rc addr; + socklen_t sa_len; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (sock < 0) { + error("server socket: %s (%d)", strerror(errno), errno); + return NULL; + } + + lm = RFCOMM_LM_SECURE; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { + error("server setsockopt: %s (%d)", strerror(errno), errno); + close(sock); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + bacpy(&addr.rc_bdaddr, BDADDR_ANY); + addr.rc_channel = channel ? *channel : 0; + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 1) < 0) { + error("server listen: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + sa_len = sizeof(struct sockaddr_rc); + getsockname(sock, (struct sockaddr *) &addr, &sa_len); + *channel = addr.rc_channel; + + io = g_io_channel_unix_new(sock); + if (!io) { + error("Unable to allocate new io channel"); + close(sock); + return NULL; + } + + return io; +} + +static int server_init(DBusConnection *conn, gboolean no_hfp) +{ + uint8_t chan = DEFAULT_HS_AG_CHANNEL; + sdp_buf_t buf; + + hs_server = server_socket(&chan); + if (!hs_server) + return -1; + + if (hsp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hs_record_id = add_record(chan, &buf); + free(buf.data); + if (!hs_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hs_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) ag_io_cb, NULL); + + if (no_hfp) + return 0; + + chan = DEFAULT_HF_AG_CHANNEL; + + hf_server = server_socket(&chan); + if (!hf_server) + return -1; + + if (hfp_ag_record(&buf, chan) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + hf_record_id = add_record(chan, &buf); + free(buf.data); + if (!hf_record_id) { + error("Unable to register HS AG service record"); + g_io_channel_unref(hf_server); + hs_server = NULL; + return -1; + } + + g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) ag_io_cb, NULL); + + return 0; +} + +static void server_exit(void) +{ + if (hs_record_id) { + remove_record(hs_record_id); + hs_record_id = 0; + } + + if (hs_server) { + g_io_channel_unref(hs_server); + hs_server = NULL; + } + + if (hf_record_id) { + remove_record(hf_record_id); + hf_record_id = 0; + } + + if (hf_server) { + g_io_channel_unref(hf_server); + hf_server = NULL; + } +} + +int audio_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) { + connection = dbus_connection_ref(conn); + + if (server_init(conn, no_hfp) < 0) + goto failed; + if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, NULL, manager_unregister)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - return -1; + goto failed; } if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, @@ -1206,22 +1730,23 @@ int audio_init(DBusConnection *conn) manager_signals, NULL)) { error("Failed to register %s interface to %s", AUDIO_MANAGER_INTERFACE, AUDIO_MANAGER_PATH); - dbus_connection_destroy_object_path(conn, - AUDIO_MANAGER_PATH); - return -1; + goto failed; } - connection = dbus_connection_ref(conn); - info("Registered manager path:%s", AUDIO_MANAGER_PATH); register_stored(); return 0; +failed: + audio_exit(); + return -1; } void audio_exit(void) { + server_exit(); + dbus_connection_destroy_object_path(connection, AUDIO_MANAGER_PATH); dbus_connection_unref(connection); diff --git a/audio/manager.h b/audio/manager.h index f21e958e..79fe9090 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -29,7 +29,7 @@ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -int audio_init(DBusConnection *conn); +int audio_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci); void audio_exit(void); -- cgit From 45164afdd713dabd8389834a0be0edd006a588ba Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 23 Jul 2007 09:43:41 +0000 Subject: Remove non-critical error print --- audio/headset.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index e97a99cf..3af4d05a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -208,10 +208,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, &bytes_read); - if (err != G_IO_ERROR_NONE) { - error("Ignoring error %d", err); + if (err != G_IO_ERROR_NONE) return TRUE; - } free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; -- cgit From 30a072da30080032ba9b54d9651b0771ba421846 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 23 Jul 2007 13:00:21 +0000 Subject: Small coding style fixes --- audio/headset.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 3af4d05a..c8de6cf4 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -341,6 +341,7 @@ static void pending_connect_ok(struct pending_connect *c, struct device *dev) } else if (c->pkt) headset_get_config(dev, c->sock, c->pkt); + pending_connect_free(c); } @@ -984,10 +985,12 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, c->msg = dbus_message_ref(msg); - if ((err = rfcomm_connect(device, c)) < 0) + err = rfcomm_connect(device, c); + if (err < 0) goto error; return DBUS_HANDLER_RESULT_HANDLED; + error: pending_connect_free(c); return err_connect_failed(conn, msg, strerror(-err)); @@ -1384,7 +1387,8 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) goto error; c->sock = sock; c->pkt = pkt; - if ((err = rfcomm_connect(device, c)) < 0) + err = rfcomm_connect(device, c); + if (err = < 0) goto error; return 0; } @@ -1394,7 +1398,8 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) goto error; c->sock = sock; c->pkt = pkt; - if ((err = sco_connect(device, c)) < 0) + err = sco_connect(device, c); + if (err < 0) goto error; return 0; } @@ -1409,6 +1414,7 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) cfg->rate = 8000; return 0; + error: if (c) pending_connect_free(c); -- cgit From c5acbd150f5addb50a8c16df7afed6aaddc283ee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 23 Jul 2007 15:41:43 +0000 Subject: Fix typo in previous coding style fix --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c8de6cf4..9dccc5ad 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1388,7 +1388,7 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt) c->sock = sock; c->pkt = pkt; err = rfcomm_connect(device, c); - if (err = < 0) + if (err < 0) goto error; return 0; } -- cgit From 6eb1dc5328b10dc8bece447947f36b1504357a35 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 23 Jul 2007 15:43:32 +0000 Subject: Add first files for to start A2DP implementation --- audio/Makefile.am | 3 ++- audio/avdtp.c | 26 ++++++++++++++++++++++++++ audio/avdtp.h | 23 +++++++++++++++++++++++ audio/device.h | 3 +-- audio/sink.c | 28 ++++++++++++++++++++++++++++ audio/sink.h | 27 +++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 audio/avdtp.c create mode 100644 audio/avdtp.h create mode 100644 audio/sink.c create mode 100644 audio/sink.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 84242544..37acdf70 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -12,7 +12,8 @@ service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ - error.h error.c device.h device.c gateway.h gateway.c + error.h error.c device.h device.c gateway.h gateway.c \ + sink.c sink.h avdtp.c avdtp.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ diff --git a/audio/avdtp.c b/audio/avdtp.c new file mode 100644 index 00000000..2f4ab59e --- /dev/null +++ b/audio/avdtp.c @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 diff --git a/audio/avdtp.h b/audio/avdtp.h new file mode 100644 index 00000000..153a1731 --- /dev/null +++ b/audio/avdtp.h @@ -0,0 +1,23 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + diff --git a/audio/device.h b/audio/device.h index 12e15806..71e1053f 100644 --- a/audio/device.h +++ b/audio/device.h @@ -25,6 +25,7 @@ #include "headset.h" #include "gateway.h" +#include "sink.h" #define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" @@ -45,12 +46,10 @@ #define AVRCP_TARGET_UUID "0000110C-0000-1000-8000-00805F9B34FB" /* Move these to respective .h files once they exist */ -#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" #define AUDIO_SOURCE_INTERFACE "org.bluez.audio.Source" #define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" #define AUDIO_TARGET_INTERFACE "org.bluez.audio.Target" -struct sink; struct source; struct control; struct target; diff --git a/audio/sink.c b/audio/sink.c new file mode 100644 index 00000000..436dfbce --- /dev/null +++ b/audio/sink.c @@ -0,0 +1,28 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "avdtp.h" diff --git a/audio/sink.h b/audio/sink.h new file mode 100644 index 00000000..4cc3a0e4 --- /dev/null +++ b/audio/sink.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + +#define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" + +struct sink; + -- cgit From 8fb78ad3b3acc6cf76c7a1ba5f6062f126d1b7a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 25 Jul 2007 12:57:45 +0000 Subject: Fix NotAvailable error return --- audio/error.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/error.c b/audio/error.c index 6bd6e08d..275eb2a7 100644 --- a/audio/error.c +++ b/audio/error.c @@ -93,7 +93,8 @@ DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) { - return error_reply(conn, msg, ".NotAvailable", "Not available"); + return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotAvailable", + "Not available"); } DBusHandlerResult err_failed(DBusConnection *conn, -- cgit From e929f72c17c104ed4eb6c15bda8f5d2be58c8084 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 1 Aug 2007 19:37:08 +0000 Subject: Add files for A2DP specific code --- audio/Makefile.am | 2 +- audio/a2dp.c | 28 ++++++++++++++++++++++++++++ audio/a2dp.h | 23 +++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 audio/a2dp.c create mode 100644 audio/a2dp.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 37acdf70..cc73f9be 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -13,7 +13,7 @@ service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ error.h error.c device.h device.c gateway.h gateway.c \ - sink.c sink.h avdtp.c avdtp.h + sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ diff --git a/audio/a2dp.c b/audio/a2dp.c new file mode 100644 index 00000000..db0ed7cb --- /dev/null +++ b/audio/a2dp.c @@ -0,0 +1,28 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 "a2dp.h" diff --git a/audio/a2dp.h b/audio/a2dp.h new file mode 100644 index 00000000..153a1731 --- /dev/null +++ b/audio/a2dp.h @@ -0,0 +1,23 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * 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 + * 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 + * + */ + -- cgit From 6763ebb3c231740c66a235f94d56e8d8cc213d90 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 11:05:24 +0000 Subject: Integrate A2DP work from Johan's and Luiz's GIT trees --- audio/a2dp.c | 611 ++++++++++++++ audio/a2dp.h | 48 ++ audio/avdtp.c | 2174 +++++++++++++++++++++++++++++++++++++++++++++++++ audio/avdtp.h | 177 ++++ audio/device.c | 207 ++++- audio/device.h | 7 + audio/headset.c | 99 ++- audio/headset.h | 24 +- audio/ipc.h | 106 ++- audio/main.c | 48 +- audio/manager.c | 63 +- audio/manager.h | 17 +- audio/pcm_bluetooth.c | 541 +++++++++--- audio/sink.c | 429 ++++++++++ audio/sink.h | 11 + audio/unix.c | 227 ++++-- audio/unix.h | 5 +- 17 files changed, 4451 insertions(+), 343 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index db0ed7cb..3afc133d 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -25,4 +25,615 @@ #include #endif +#include + +#include +#include + +#include +#include + +#include "logging.h" +#include "manager.h" +#include "avdtp.h" #include "a2dp.h" + +static DBusConnection *connection = NULL; + +static uint32_t sink_record_id = 0; +static uint32_t source_record_id = 0; + +static struct avdtp_local_sep *sink_sep = NULL; +static struct avdtp_local_sep *source_sep = NULL; + +static gboolean setconf_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t int_seid, GSList *caps, + uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Set_Configuration_Ind"); + else + debug("SBC Source: Set_Configuration_Ind"); + + return TRUE; +} + +static gboolean getcap_ind(struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap; + + if (sep == sink_sep) + debug("SBC Sink: Get_Capability_Ind"); + else + debug("SBC Source: Get_Capability_Ind"); + + *caps = NULL; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + memset(&sbc_cap, 0, sizeof(struct sbc_codec_cap)); + + sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; + + sbc_cap.frequency = ( A2DP_SAMPLING_FREQ_48000 | + A2DP_SAMPLING_FREQ_44100 | + A2DP_SAMPLING_FREQ_32000 | + A2DP_SAMPLING_FREQ_16000 ); + + sbc_cap.channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | + A2DP_CHANNEL_MODE_STEREO | + A2DP_CHANNEL_MODE_DUAL_CHANNEL | + A2DP_CHANNEL_MODE_MONO ); + + sbc_cap.block_length = ( A2DP_BLOCK_LENGTH_16 | + A2DP_BLOCK_LENGTH_12 | + A2DP_BLOCK_LENGTH_8 | + A2DP_BLOCK_LENGTH_4 ); + + sbc_cap.subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + + sbc_cap.allocation_method = ( A2DP_ALLOCATION_LOUDNESS | + A2DP_ALLOCATION_SNR ); + + sbc_cap.min_bitpool = 2; + sbc_cap.max_bitpool = 250; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + *caps = g_slist_append(*caps, media_codec); + + return TRUE; +} + +static void setconf_cfm(struct avdtp_local_sep *sep, + struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Set_Configuration_Cfm"); + else + debug("SBC Source: Set_Configuration_Cfm"); +} + +static gboolean getconf_ind(struct avdtp_local_sep *sep, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Get_Configuration_Ind"); + else + debug("SBC Source: Get_Configuration_Ind"); + return TRUE; +} + +static void getconf_cfm(struct avdtp_local_sep *sep, + struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Set_Configuration_Cfm"); + else + debug("SBC Source: Set_Configuration_Cfm"); +} + +static gboolean open_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Open_Ind"); + else + debug("SBC Source: Open_Ind"); + return TRUE; +} + +static void open_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Open_Cfm"); + else + debug("SBC Source: Open_Cfm"); +} + +static gboolean start_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Start_Ind"); + else + debug("SBC Source: Start_Ind"); + return TRUE; +} + +static void start_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Start_Cfm"); + else + debug("SBC Source: Start_Cfm"); +} + +static gboolean suspend_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Suspend_Ind"); + else + debug("SBC Source: Suspend_Ind"); + return TRUE; +} + +static void suspend_cfm(struct avdtp_local_sep *sep, + struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Suspend_Cfm"); + else + debug("SBC Source: Suspend_Cfm"); +} + +static gboolean close_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Close_Ind"); + else + debug("SBC Source: Close_Ind"); + return TRUE; +} + +static void close_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Close_Cfm"); + else + debug("SBC Source: Close_Cfm"); +} + +static gboolean abort_ind(struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: Abort_Ind"); + else + debug("SBC Source: Abort_Ind"); + return TRUE; +} + +static void abort_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +{ + if (sep == sink_sep) + debug("SBC Sink: Abort_Cfm"); + else + debug("SBC Source: Abort_Cfm"); +} + +static gboolean reconf_ind(struct avdtp_local_sep *sep, uint8_t *err) +{ + if (sep == sink_sep) + debug("SBC Sink: ReConfigure_Ind"); + else + debug("SBC Source: ReConfigure_Ind"); + return TRUE; +} + +static void reconf_cfm(struct avdtp_local_sep *sep) +{ + if (sep == sink_sep) + debug("SBC Sink: ReConfigure_Cfm"); + else + debug("SBC Source: ReConfigure_Cfm"); +} + +static struct avdtp_sep_cfm cfm = { + .set_configuration = setconf_cfm, + .get_configuration = getconf_cfm, + .open = open_cfm, + .start = start_cfm, + .suspend = suspend_cfm, + .close = close_cfm, + .abort = abort_cfm, + .reconfigure = reconf_cfm +}; + +static struct avdtp_sep_ind ind = { + .get_capability = getcap_ind, + .set_configuration = setconf_ind, + .get_configuration = getconf_ind, + .open = open_ind, + .start = start_ind, + .suspend = suspend_ind, + .close = close_ind, + .abort = abort_ind, + .reconfigure = reconf_ind +}; + +static int a2dp_source_record(sdp_buf_t *buf) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap, avdtp, a2src; + 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 = AVDTP_UUID, ver = 0x0100, 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); + + sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID); + svclass_id = sdp_list_append(0, &a2src); + sdp_set_service_classes(&record, svclass_id); + + sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(0, &profile[0]); + sdp_set_profile_descs(&record, pfseq); + + 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(&avdtp, AVDTP_UUID); + proto[1] = sdp_list_append(0, &avdtp); + 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); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(&record, "Audio Source", 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 a2dp_sink_record(sdp_buf_t *buf) +{ + return 0; +} + +int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +{ + sdp_buf_t buf; + + if (!enable_sink && !enable_source) + return 0; + + connection = dbus_connection_ref(conn); + + avdtp_init(); + + if (enable_sink) { + source_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, + AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm); + if (source_sep == NULL) + return -1; + + if (a2dp_source_record(&buf) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + source_record_id = add_service_record(conn, &buf); + free(buf.data); + if (!source_record_id) { + error("Unable to register A2DP Source service record"); + return -1; + } + } + + if (enable_source) { + sink_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, + AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm); + if (sink_sep == NULL) + return -1; + + if (a2dp_sink_record(&buf) < 0) { + error("Unable to allocate new service record"); + return -1; + } + + sink_record_id = add_service_record(conn, &buf); + free(buf.data); + if (!sink_record_id) { + error("Unable to register A2DP Sink service record"); + return -1; + } + } + + return 0; +} + +void a2dp_exit() +{ + if (sink_sep) + avdtp_unregister_sep(sink_sep); + + if (source_sep) + avdtp_unregister_sep(source_sep); + + if (source_record_id) { + remove_service_record(connection, source_record_id); + source_record_id = 0; + } + + if (sink_record_id) { + remove_service_record(connection, sink_record_id); + sink_record_id = 0; + } + + dbus_connection_unref(connection); +} + +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { + switch (freq) { + case A2DP_SAMPLING_FREQ_16000: + case A2DP_SAMPLING_FREQ_32000: + return 53; + case A2DP_SAMPLING_FREQ_44100: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 31; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 53; + default: + error("Invalid channel mode %u", mode); + return 53; + } + case A2DP_SAMPLING_FREQ_48000: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 29; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 51; + default: + error("Invalid channel mode %u", mode); + return 51; + } + default: + error("Invalid sampling freq %u", freq); + return 53; + } +} + +static gboolean select_sbc_params(struct sbc_codec_cap *cap, + struct sbc_codec_cap *supported) +{ + uint max_bitpool, min_bitpool; + + memset(cap, 0, sizeof(struct sbc_codec_cap)); + + cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + cap->cap.media_codec_type = A2DP_CODEC_SBC; + + if (supported->frequency & A2DP_SAMPLING_FREQ_48000) + cap->frequency = A2DP_SAMPLING_FREQ_48000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) + cap->frequency = A2DP_SAMPLING_FREQ_44100; + else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) + cap->frequency = A2DP_SAMPLING_FREQ_32000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) + cap->frequency = A2DP_SAMPLING_FREQ_16000; + else { + error("No supported frequencies"); + return FALSE; + } + + if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) + cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + else { + error("No supported channel modes"); + return FALSE; + } + + if (supported->block_length & A2DP_BLOCK_LENGTH_16) + cap->block_length = A2DP_BLOCK_LENGTH_16; + else if (supported->block_length & A2DP_BLOCK_LENGTH_12) + cap->block_length = A2DP_BLOCK_LENGTH_12; + else if (supported->block_length & A2DP_BLOCK_LENGTH_8) + cap->block_length = A2DP_BLOCK_LENGTH_8; + else if (supported->block_length & A2DP_BLOCK_LENGTH_4) + cap->block_length = A2DP_BLOCK_LENGTH_4; + else { + error("No supported block lengths"); + return FALSE; + } + + if (supported->subbands & A2DP_SUBBANDS_8) + cap->subbands = A2DP_SUBBANDS_8; + else if (supported->subbands & A2DP_SUBBANDS_4) + cap->subbands = A2DP_SUBBANDS_4; + else { + error("No supported subbands"); + return FALSE; + } + + if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) + cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + else if (supported->allocation_method & A2DP_ALLOCATION_SNR) + cap->allocation_method = A2DP_ALLOCATION_SNR; + + min_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + supported->min_bitpool); + max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + supported->max_bitpool); + + cap->min_bitpool = min_bitpool; + cap->max_bitpool = max_bitpool; + + return TRUE; +} + +gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap, *acp_sbc; + + media_codec = avdtp_get_codec(rsep); + if (!media_codec) + return FALSE; + + acp_sbc = (void *) media_codec->data; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + select_sbc_params(&sbc_cap, acp_sbc); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + *caps = g_slist_append(*caps, media_codec); + + return TRUE; +} + +gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg) +{ + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap = NULL; + struct sbc_codec_cap *sbc_cap; + struct ipc_data_cfg *rsp; + struct ipc_codec_sbc *sbc; + GSList *caps; + + rsp = g_malloc0(sizeof(struct ipc_data_cfg) + + sizeof(struct ipc_codec_sbc)); + rsp->fd = -1; + sbc = (void *) rsp->data; + + if (!avdtp_stream_get_transport(stream, &rsp->fd, &rsp->pkt_len, + &caps)) { + g_free(rsp); + return FALSE; + } + + for (; caps; caps = g_slist_next(caps)) { + cap = caps->data; + if (cap->category == AVDTP_MEDIA_CODEC) { + codec_cap = (void *) cap->data; + break; + } + } + + if (codec_cap == NULL) { + g_free(rsp); + return FALSE; + } + + rsp->fd_opt = CFG_FD_OPT_WRITE; + + *cfg = rsp; + + if (codec_cap->media_codec_type != A2DP_CODEC_SBC) + return TRUE; + + sbc_cap = (struct sbc_codec_cap *) codec_cap; + rsp->channels = sbc_cap->channel_mode == + A2DP_CHANNEL_MODE_MONO ? 1 : 2; + rsp->channel_mode = sbc_cap->channel_mode; + rsp->sample_size = 2; + + switch (sbc_cap->frequency) { + case A2DP_SAMPLING_FREQ_16000: + rsp->rate = 16000; + break; + case A2DP_SAMPLING_FREQ_32000: + rsp->rate = 32000; + break; + case A2DP_SAMPLING_FREQ_44100: + rsp->rate = 44100; + break; + case A2DP_SAMPLING_FREQ_48000: + rsp->rate = 48000; + break; + } + + rsp->codec = CFG_CODEC_SBC; + sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? + 0x01 : 0x00; + sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; + + switch (sbc_cap->block_length) { + case A2DP_BLOCK_LENGTH_4: + sbc->blocks = 4; + break; + case A2DP_BLOCK_LENGTH_8: + sbc->blocks = 8; + break; + case A2DP_BLOCK_LENGTH_12: + sbc->blocks = 12; + break; + case A2DP_BLOCK_LENGTH_16: + sbc->blocks = 16; + break; + } + + sbc->bitpool = sbc_cap->max_bitpool; + + return TRUE; +} diff --git a/audio/a2dp.h b/audio/a2dp.h index 153a1731..7e53f7a9 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -21,3 +21,51 @@ * */ +#include +#include +#include "avdtp.h" + +#define A2DP_CODEC_SBC 0x00 +#define A2DP_CODEC_MPEG12 0x01 +#define A2DP_CODEC_MPEG24 0x02 +#define A2DP_CODEC_ATRAC 0x03 + +#define A2DP_SAMPLING_FREQ_16000 (1 << 3) +#define A2DP_SAMPLING_FREQ_32000 (1 << 2) +#define A2DP_SAMPLING_FREQ_44100 (1 << 1) +#define A2DP_SAMPLING_FREQ_48000 1 + +#define A2DP_CHANNEL_MODE_MONO (1 << 3) +#define A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define A2DP_CHANNEL_MODE_STEREO (1 << 1) +#define A2DP_CHANNEL_MODE_JOINT_STEREO 1 + +#define A2DP_BLOCK_LENGTH_4 (1 << 3) +#define A2DP_BLOCK_LENGTH_8 (1 << 2) +#define A2DP_BLOCK_LENGTH_12 (1 << 1) +#define A2DP_BLOCK_LENGTH_16 1 + +#define A2DP_SUBBANDS_4 (1 << 1) +#define A2DP_SUBBANDS_8 1 + +#define A2DP_ALLOCATION_SNR (1 << 1) +#define A2DP_ALLOCATION_LOUDNESS 1 + +struct sbc_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t channel_mode:4; + uint8_t frequency:4; + uint8_t allocation_method:2; + uint8_t subbands:2; + uint8_t block_length:4; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)); + +int a2dp_init(DBusConnection *conn, gboolean enable_sink, + gboolean enable_source); +void a2dp_exit(void); + +gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); + +gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg); diff --git a/audio/avdtp.c b/audio/avdtp.c index 2f4ab59e..8615a18a 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -24,3 +24,2177 @@ #ifdef HAVE_CONFIG_H #include #endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "avdtp.h" +#include "logging.h" +#include "dbus.h" + +#include + +#define AVDTP_PSM 25 + +#define MAX_SEID 0x3E + +#define AVDTP_DISCOVER 0x01 +#define AVDTP_GET_CAPABILITIES 0x02 +#define AVDTP_SET_CONFIGURATION 0x03 +#define AVDTP_GET_CONFIGURATION 0x04 +#define AVDTP_RECONFIGURE 0x05 +#define AVDTP_OPEN 0x06 +#define AVDTP_START 0x07 +#define AVDTP_CLOSE 0x08 +#define AVDTP_SUSPEND 0x09 +#define AVDTP_ABORT 0x0A +#define AVDTP_SECURITY_CONTROL 0x0B + +#define AVDTP_PKT_TYPE_SINGLE 0x00 +#define AVDTP_PKT_TYPE_START 0x01 +#define AVDTP_PKT_TYPE_CONTINUE 0x02 +#define AVDTP_PKT_TYPE_END 0x03 + +#define AVDTP_MSG_TYPE_COMMAND 0x00 +#define AVDTP_MSG_TYPE_ACCEPT 0x02 +#define AVDTP_MSG_TYPE_REJECT 0x03 + +#define REQ_TIMEOUT 2000 +#define DISCONNECT_TIMEOUT 5000 + +typedef enum { + AVDTP_ERROR_ERRNO, + AVDTP_ERROR_ERROR_CODE +} avdtp_error_type_t; + +typedef enum { + AVDTP_SESSION_STATE_DISCONNECTED, + AVDTP_SESSION_STATE_CONNECTING, + AVDTP_SESSION_STATE_CONNECTED +} avdtp_session_state_t; + +struct avdtp_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +typedef struct seid_info { + uint8_t rfa0:1; + uint8_t inuse:1; + uint8_t seid:6; + uint8_t rfa2:3; + uint8_t type:1; + uint8_t media_type:4; +} __attribute__ ((packed)) seid_info_t; + +/* packets */ + +struct discover_req { + struct avdtp_header header; +} __attribute__ ((packed)); + +struct discover_resp { + struct avdtp_header header; + struct seid_info seps[0]; +} __attribute__ ((packed)); + +struct getcap_resp { + struct avdtp_header header; + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct seid_req { + struct avdtp_header header; + uint8_t rfa0:2; + uint8_t acp_seid:6; +} __attribute__ ((packed)); + +struct seid_rej { + struct avdtp_header header; + uint8_t error; +} __attribute__ ((packed)); + +struct setconf_req { + struct avdtp_header header; + + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t rfa1:2; + uint8_t int_seid:6; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct setconf_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + +struct conf_rej { + struct avdtp_header header; + uint8_t category; + uint8_t error; +} __attribute__ ((packed)); + +struct stream_rej { + struct avdtp_header header; + uint8_t rfa0; + uint8_t acp_seid:6; + uint8_t error_code; +} __attribute__ ((packed)); + +struct reconf_req { + struct avdtp_header header; + + uint8_t rfa0:2; + uint8_t acp_seid:6; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct reconf_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + +struct abort_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + +struct stream_pause_resp { + struct avdtp_header header; + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t error; +} __attribute__ ((packed)); + +struct avdtp_general_rej { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t rfa0; +} __attribute__ ((packed)); + +struct pending_req { + struct avdtp_header *msg; + int msg_size; + struct avdtp_stream *stream; /* Set if the request targeted a stream */ + guint timeout; +}; + +struct avdtp_remote_sep { + uint8_t seid; + uint8_t type; + uint8_t media_type; + struct avdtp_service_capability *codec; + GSList *caps; /* of type struct avdtp_service_capability */ + struct avdtp_stream *stream; +}; + +struct avdtp_local_sep { + avdtp_state_t state; + struct avdtp_stream *stream; + struct seid_info info; + uint8_t codec; + GSList *caps; + struct avdtp_sep_ind *ind; + struct avdtp_sep_cfm *cfm; + void *data; +}; + +struct avdtp_stream { + int sock; + uint16_t mtu; + struct avdtp *session; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + GSList *caps; + avdtp_stream_state_cb cb; + void *user_data; + guint io; /* Transport GSource ID */ + guint close_timer; /* Waiting for other side to close transport */ + gboolean open_acp; /* If we are in ACT role for Open */ + gboolean close_int; /* If we are in INT role for Close */ +}; + +/* Structure describing an AVDTP connection between two devices */ +struct avdtp { + int ref; + + bdaddr_t src; + bdaddr_t dst; + + avdtp_session_state_t last_state; + avdtp_session_state_t state; + + guint io; + int sock; + + GSList *seps; /* Elements of type struct avdtp_remote_sep * */ + + GSList *streams; /* Elements of type struct avdtp_stream * */ + + GSList *req_queue; /* Elements of type struct pending_req * */ + GSList *prio_queue; /* Same as req_queue but is processed before it */ + + struct avdtp_stream *pending_open; + + uint16_t mtu; + char *buf; + + avdtp_discover_cb_t discov_cb; + void *user_data; + + struct pending_req *req; + + guint dc_timer; +}; + +struct avdtp_error { + avdtp_error_type_t type; + union { + uint8_t error_code; + int posix_errno; + } err; +}; + +static uint8_t free_seid = 1; +static GSList *local_seps = NULL; + +static GIOChannel *avdtp_server = NULL; + +static GSList *sessions = NULL; + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, void *buffer, int size); +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *header, int size); +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *header, int size); +static int process_queue(struct avdtp *session); + +static const char *avdtp_statestr(avdtp_state_t state) +{ + switch (state) { + case AVDTP_STATE_IDLE: + return "IDLE"; + case AVDTP_STATE_CONFIGURED: + return "CONFIGURED"; + case AVDTP_STATE_OPEN: + return "OPEN"; + case AVDTP_STATE_STREAMING: + return "STREAMING"; + case AVDTP_STATE_CLOSING: + return "CLOSING"; + case AVDTP_STATE_ABORTING: + return "ABORTING"; + default: + return ""; + } +} + +static gboolean avdtp_send(struct avdtp *session, void *data, int len) +{ + int ret; + + ret = send(session->sock, data, len, 0); + + if (ret < 0) + ret = -errno; + else if (ret != len) + ret = -EIO; + + if (ret < 0) { + error("avdtp_send: %s (%d)", strerror(-ret), -ret); + return FALSE; + } + + return TRUE; +} + +static void pending_req_free(struct pending_req *req) +{ + if (req->timeout) + g_source_remove(req->timeout); + g_free(req->msg); + g_free(req); +} + +#if 0 +static gboolean stream_close_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + close(stream->sock); + + return FALSE; +} +#endif + +static gboolean disconnect_timeout(gpointer user_data) +{ + struct avdtp *session = user_data; + + assert(session->ref == 1); + + sessions = g_slist_remove(sessions, session); + + session->dc_timer = 0; + + avdtp_unref(session); + + return FALSE; +} + +static void remove_disconnect_timer(struct avdtp *session) +{ + g_source_remove(session->dc_timer); + session->dc_timer = 0; +} + +static void set_disconnect_timer(struct avdtp *session) +{ + if (session->dc_timer) + remove_disconnect_timer(session); + + session->dc_timer = g_timeout_add(DISCONNECT_TIMEOUT, + disconnect_timeout, session); +} + +static void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) +{ + err->type = type; + switch (type) { + case AVDTP_ERROR_ERRNO: + err->err.posix_errno = id; + break; + case AVDTP_ERROR_ERROR_CODE: + err->err.error_code = id; + break; + } +} + +static void stream_free(struct avdtp_stream *stream) +{ + stream->lsep->info.inuse = 0; + stream->lsep->stream = NULL; + stream->rsep->stream = NULL; + if (stream->caps) { + g_slist_foreach(stream->caps, (GFunc) g_free, NULL); + g_slist_free(stream->caps); + } + g_free(stream); +} + +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state) +{ + struct avdtp_stream *stream = sep->stream; + avdtp_state_t old_state; + + if (sep->state == state) + return; + + debug("stream state changed: %s -> %s", avdtp_statestr(sep->state), + avdtp_statestr(state)); + + old_state = sep->state; + sep->state = state; + + if (stream && stream->cb) + stream->cb(stream, old_state, state, NULL, + stream->user_data); + + if (state == AVDTP_STATE_IDLE) { + session->streams = g_slist_remove(session->streams, stream); + stream_free(stream); + } +} + +static void finalize_discovery(struct avdtp *session, int err) +{ + if (!session->discov_cb) + return; + + session->discov_cb(session, session->seps, err, + session->user_data); + + session->discov_cb = NULL; + session->user_data = NULL; +} + +static void release_stream(struct avdtp_stream *stream, struct avdtp *session) +{ + if (stream->sock >= 0) + close(stream->sock); + if (stream->io) + g_source_remove(stream->io); + avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_IDLE); +} + +static void connection_lost(struct avdtp *session) +{ + if (session->state == AVDTP_SESSION_STATE_CONNECTED) { + char address[18]; + + ba2str(&session->dst, address); + debug("Disconnected from %s", address); + } + + if (session->discov_cb) + finalize_discovery(session, -ECONNABORTED); + + if (session->sock >= 0) { + close(session->sock); + session->sock = -1; + } + + session->state = AVDTP_SESSION_STATE_DISCONNECTED; + + if (session->io) { + g_source_remove(session->io); + session->io = 0; + } + + g_slist_foreach(session->streams, (GFunc) release_stream, session); + session->streams = NULL; +} + +void avdtp_unref(struct avdtp *session) +{ + if (!session) + return; + + if (!g_slist_find(sessions, session)) { + error("avdtp_unref: trying to unref a unknown session"); + return; + } + + session->ref--; + + debug("avdtp_unref: ref=%d", session->ref); + + if (session->ref == 1) { + if (session->dc_timer) + remove_disconnect_timer(session); + if (session->sock >= 0) + set_disconnect_timer(session); + return; + } + + if (session->ref > 0) + return; + + if (session->dc_timer) + remove_disconnect_timer(session); + + connection_lost(session); + + sessions = g_slist_remove(sessions, session); + + if (session->req) + pending_req_free(session->req); + + g_slist_foreach(session->seps, (GFunc) g_free, NULL); + g_slist_free(session->seps); + + g_free(session->buf); + + g_free(session); +} + +struct avdtp *avdtp_ref(struct avdtp *session) +{ + session->ref++; + debug("avdtp_ref: ref=%d", session->ref); + if (session->dc_timer) + remove_disconnect_timer(session); + return session; +} + +static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid) +{ + GSList *l; + + for (l = local_seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_local_sep *sep = l->data; + + if (sep->info.seid == seid) + return sep; + } + + return NULL; +} + +static struct avdtp_local_sep *find_local_sep(uint8_t type, uint8_t media_type, + uint8_t codec) +{ + GSList *l; + + for (l = local_seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_local_sep *sep = l->data; + + if (sep->info.inuse) + continue; + + if (sep->info.type == type && + sep->info.media_type == media_type && + sep->codec == codec) + return sep; + } + + return NULL; +} + +static void init_response(struct avdtp_header *rsp, struct avdtp_header *req, + gboolean accept) +{ + rsp->packet_type = AVDTP_PKT_TYPE_SINGLE; + rsp->message_type = accept ? AVDTP_MSG_TYPE_ACCEPT : + AVDTP_MSG_TYPE_REJECT; + rsp->transaction = req->transaction; + rsp->signal_id = req->signal_id; + rsp->rfa0 = 0; +} + +static gboolean avdtp_unknown_cmd(struct avdtp *session, + struct avdtp_header *req, int size) +{ + struct avdtp_general_rej rej; + + memset(&rej, 0, sizeof(rej)); + + rej.packet_type = AVDTP_PKT_TYPE_SINGLE; + rej.message_type = AVDTP_MSG_TYPE_REJECT; + rej.transaction = req->transaction; + + return avdtp_send(session, &rej, sizeof(rej)); +} + +static gboolean avdtp_discover_cmd(struct avdtp *session, + struct discover_req *req, int size) +{ + GSList *l; + struct discover_resp *rsp = (struct discover_resp *) session->buf; + struct seid_info *info; + int rsp_size; + + init_response(&rsp->header, &req->header, TRUE); + rsp_size = sizeof(struct discover_resp); + info = rsp->seps; + + for (l = local_seps; l != NULL; l = l->next) { + struct avdtp_local_sep *sep = l->data; + + if (rsp_size + sizeof(struct seid_info) > session->mtu) + break; + + memcpy(&info, &sep->info, sizeof(struct seid_info)); + rsp_size += sizeof(struct seid_info); + info++; + } + + return avdtp_send(session, session->buf, rsp_size); +} + +static gboolean avdtp_getcap_cmd(struct avdtp *session, + struct seid_req *req, int size) +{ + GSList *l, *caps; + struct avdtp_local_sep *sep = NULL; + struct seid_rej rej; + struct getcap_resp *rsp = (struct getcap_resp *) session->buf; + int rsp_size; + unsigned char *ptr; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short getcap request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (!sep->ind->get_capability(sep, &caps, &err)) + goto failed; + + init_response(&rsp->header, &req->header, TRUE); + rsp_size = sizeof(struct getcap_resp); + ptr = rsp->caps; + + for (l = caps; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (rsp_size + cap->length + 2 > session->mtu) + break; + + memcpy(ptr, cap, cap->length + 2); + rsp_size += cap->length + 2; + ptr += cap->length + 2; + + g_free(cap); + } + + g_slist_free(caps); + + return avdtp_send(session, session->buf, rsp_size); + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = AVDTP_BAD_ACP_SEID; + return avdtp_send(session, &rej, sizeof(rej)); +} + +static gboolean avdtp_setconf_cmd(struct avdtp *session, + struct setconf_req *req, int size) +{ + struct conf_rej rej; + struct setconf_resp *rsp = (struct setconf_resp *) session->buf; + struct avdtp_local_sep *lsep; + gboolean ret; + + if (size < sizeof(struct setconf_req)) { + error("Too short getcap request"); + return FALSE; + } + + lsep = find_local_sep_by_seid(req->acp_seid); + if (!lsep || !lsep->stream) { + init_response(&rej.header, &req->header, FALSE); + rej.error = AVDTP_BAD_ACP_SEID; + return avdtp_send(session, &rej, sizeof(rej)); + } + + init_response(&rsp->header, &req->header, TRUE); + + ret = avdtp_send(session, rsp, sizeof(struct setconf_req)); + + if (ret) + avdtp_sep_set_state(session, lsep, AVDTP_STATE_CONFIGURED); + + return ret; +} + +static gboolean avdtp_getconf_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_reconf_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_start_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_suspend_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + struct avdtp_local_sep *sep; + struct abort_resp *rsp = (struct abort_resp *) session->buf; + struct seid_rej rej; + uint8_t err; + gboolean ret; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->ind && sep->ind->abort) { + if (!sep->ind->abort(sep, sep->stream, &err)) + goto failed; + } + + init_response(&rsp->header, &req->header, TRUE); + ret = avdtp_send(session, rsp, sizeof(struct abort_resp)); + + if (ret) + avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); + + return ret; + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); +} + +static gboolean avdtp_secctl_cmd(struct avdtp *session, struct seid_req *req, + int size) +{ + return avdtp_unknown_cmd(session, (void *) req, size); +} + +static gboolean avdtp_parse_cmd(struct avdtp *session, + struct avdtp_header *header, int size) +{ + switch (header->signal_id) { + case AVDTP_DISCOVER: + debug("Received DISCOVER_CMD"); + return avdtp_discover_cmd(session, (void *) header, size); + case AVDTP_GET_CAPABILITIES: + debug("Received GET_CAPABILITIES_CMD"); + return avdtp_getcap_cmd(session, (void *) header, size); + case AVDTP_SET_CONFIGURATION: + debug("Received SET_CONFIGURATION_CMD"); + return avdtp_setconf_cmd(session, (void *) header, size); + case AVDTP_GET_CONFIGURATION: + debug("Received GET_CONFIGURATION_CMD"); + return avdtp_getconf_cmd(session, (void *) header, size); + case AVDTP_RECONFIGURE: + debug("Received RECONFIGURE_CMD"); + return avdtp_reconf_cmd(session, (void *) header, size); + case AVDTP_OPEN: + debug("Received OPEN_CMD"); + return avdtp_open_cmd(session, (void *) header, size); + case AVDTP_START: + debug("Received START_CMD"); + return avdtp_start_cmd(session, (void *) header, size); + case AVDTP_CLOSE: + debug("Received CLOSE_CMD"); + return avdtp_close_cmd(session, (void *) header, size); + case AVDTP_SUSPEND: + debug("Received SUSPEND_CMD"); + return avdtp_suspend_cmd(session, (void *) header, size); + case AVDTP_ABORT: + debug("Received ABORT_CMD"); + return avdtp_abort_cmd(session, (void *) header, size); + case AVDTP_SECURITY_CONTROL: + debug("Received SECURITY_CONTROL_CMD"); + return avdtp_secctl_cmd(session, (void *) header, size); + default: + debug("Received unknown request id %u", header->signal_id); + return avdtp_unknown_cmd(session, (void *) header, size); + } +} + +static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp_stream *stream = data; + struct avdtp_local_sep *sep = stream->lsep; + + if (stream->close_int && sep->cfm && sep->cfm->close) + sep->cfm->close(sep, stream); + + avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); + + return FALSE; +} + +static void handle_transport_connect(struct avdtp *session, int sock, + uint16_t mtu) +{ + struct avdtp_stream *stream = session->pending_open; + struct avdtp_local_sep *sep = stream->lsep; + GIOChannel *channel; + + session->pending_open = NULL; + + stream->sock = sock; + stream->mtu = mtu; + + if (sep->cfm && sep->cfm->open) + sep->cfm->open(sep, stream); + + channel = g_io_channel_unix_new(stream->sock); + + stream->io = g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) transport_cb, stream); + g_io_channel_unref(channel); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); +} + +static void init_request(struct avdtp_header *header, int request_id) +{ + static int transaction = 0; + + header->packet_type = AVDTP_PKT_TYPE_SINGLE; + header->message_type = AVDTP_MSG_TYPE_COMMAND; + header->transaction = transaction; + header->signal_id = request_id; + + /* clear rfa bits */ + header->rfa0 = 0; + + transaction = (transaction + 1) % 16; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp *session = data; + struct avdtp_header *header; + gsize size; + + debug("session_cb"); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) + goto failed; + + if (g_io_channel_read(chan, session->buf, session->mtu, + &size) != G_IO_ERROR_NONE) { + error("IO Channel read error"); + goto failed; + } + + if (size < sizeof(struct avdtp_header)) { + error("Received too small packet (%d bytes)", size); + goto failed; + } + + header = (struct avdtp_header *) session->buf; + + if (header->message_type == AVDTP_MSG_TYPE_COMMAND) { + if (!avdtp_parse_cmd(session, header, size)) { + error("Unable to handle command. Disconnecting"); + goto failed; + } + + if (session->ref == 1 && !session->streams) + set_disconnect_timer(session); + + return TRUE; + } + + if (session->req == NULL) { + error("No pending request, rejecting message"); + return TRUE; + } + + if (header->transaction != session->req->msg->transaction) { + error("Transaction label doesn't match"); + return TRUE; + } + + if (header->signal_id != session->req->msg->signal_id) { + error("Reponse signal doesn't match"); + return TRUE; + } + + g_source_remove(session->req->timeout); + session->req->timeout = 0; + + switch(header->message_type) { + case AVDTP_MSG_TYPE_ACCEPT: + if (!avdtp_parse_resp(session, session->req->stream, header, + size)) { + error("Unable to parse accept response"); + goto failed; + } + break; + case AVDTP_MSG_TYPE_REJECT: + if (!avdtp_parse_rej(session, session->req->stream, header, + size)) { + error("Unable to parse reject response"); + goto failed; + } + break; + default: + error("Unknown message type"); + break; + } + + pending_req_free(session->req); + session->req = NULL; + + process_queue(session); + + return TRUE; + +failed: + connection_lost(session); + avdtp_unref(session); + return FALSE; +} + +static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp *session = data; + struct l2cap_options l2o; + socklen_t len; + int ret, err, sk; + char address[18]; + + if (cond & G_IO_NVAL) + return FALSE; + + if (!g_slist_find(sessions, session)) { + debug("l2cap_connect_cb: session got removed"); + return FALSE; + } + + if (cond & (G_IO_ERR | G_IO_HUP)) { + err = EIO; + goto failed; + } + + sk = g_io_channel_unix_get_fd(chan); + 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("connect(): %s (%d)", strerror(err), err); + goto failed; + } + + ba2str(&session->dst, address); + debug("AVDTP: connected %s channel to %s", + session->pending_open ? "transport" : "signaling", + 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; + } + + if (session->state == AVDTP_SESSION_STATE_CONNECTING) { + session->sock = sk; + session->mtu = l2o.imtu; + session->buf = g_malloc0(session->mtu); + session->state = AVDTP_SESSION_STATE_CONNECTED; + session->io = g_io_add_watch(chan, + G_IO_IN | G_IO_ERR | G_IO_HUP + | G_IO_NVAL, + (GIOFunc) session_cb, session); + } + else if (session->pending_open) + handle_transport_connect(session, sk, l2o.imtu); + + process_queue(session); + + return FALSE; + +failed: + if (session->pending_open) { + avdtp_sep_set_state(session, session->pending_open->lsep, + AVDTP_STATE_IDLE); + session->pending_open = NULL; + } else { + finalize_discovery(session, -err); + connection_lost(session); + avdtp_unref(session); + } + + return FALSE; +} + +static int l2cap_connect(struct avdtp *session) +{ + struct sockaddr_l2 l2a; + GIOChannel *io; + int sk; + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, &session->src); + + sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sk < 0) { + error("Cannot create L2CAP socket. %s(%d)", strerror(errno), + errno); + return -errno; + } + + if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + error("Bind failed. %s (%d)", strerror(errno), errno); + return -errno; + } + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + bacpy(&l2a.l2_bdaddr, &session->dst); + l2a.l2_psm = htobs(AVDTP_PSM); + + if (set_nonblocking(sk) < 0) { + error("Set non blocking: %s (%d)", strerror(errno), errno); + return -errno; + } + + io = g_io_channel_unix_new(sk); + g_io_channel_set_close_on_unref(io, FALSE); + + if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { + if (!(errno == EAGAIN || errno == EINPROGRESS)) { + error("Connect failed. %s(%d)", strerror(errno), + errno); + finalize_discovery(session, errno); + g_io_channel_close(io); + g_io_channel_unref(io); + return -errno; + } + g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) l2cap_connect_cb, session); + + if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) + session->state = AVDTP_SESSION_STATE_CONNECTING; + } else + l2cap_connect_cb(io, G_IO_OUT, session); + + g_io_channel_unref(io); + + return 0; +} + +static void queue_request(struct avdtp *session, struct pending_req *req, + gboolean priority) +{ + if (priority) + session->prio_queue = g_slist_append(session->prio_queue, req); + else + session->req_queue = g_slist_append(session->req_queue, req); +} + +static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) +{ + GSList *l; + + for (l = seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == seid) + return sep; + } + + return NULL; +} + +static gboolean request_timeout(gpointer user_data) +{ + struct avdtp *session = user_data; + struct pending_req *req; + struct seid_req sreq; + struct avdtp_remote_sep *sep; + struct avdtp_stream *stream; + uint8_t seid; + + error("Request timed out"); + + req = session->req; + session->req = NULL; + + switch (req->msg->signal_id) { + case AVDTP_DISCOVER: + case AVDTP_GET_CAPABILITIES: + case AVDTP_SET_CONFIGURATION: + case AVDTP_ABORT: + goto failed; + } + + seid = ((struct seid_req *) (req->msg))->acp_seid; + + sep = find_remote_sep(session->seps, seid); + if (!sep) { + error("Unable to find matching remote SEID %u", seid); + goto failed; + } + + stream = sep->stream; + + memset(&sreq, 0, sizeof(sreq)); + init_request(&sreq.header, AVDTP_ABORT); + sreq.acp_seid = seid; + + if (!send_request(session, TRUE, stream, &sreq, sizeof(sreq))) { + error("Unable to send abort request"); + goto failed; + } + + goto done; + +failed: + connection_lost(session); + avdtp_unref(session); +done: + pending_req_free(req); + return FALSE; +} + +static int send_req(struct avdtp *session, gboolean priority, + struct pending_req *req) +{ + int err; + + if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { + err = l2cap_connect(session); + if (err < 0) + goto failed; + } + + if (session->state < AVDTP_SESSION_STATE_CONNECTED || + session->req != NULL) { + queue_request(session, req, priority); + return 0; + } + + /* FIXME: Should we retry to send if the buffer + was not totally sent or in case of EINTR? */ + err = avdtp_send(session, req->msg, req->msg_size); + if (err < 0) + goto failed; + + session->req = req; + + req->timeout = g_timeout_add(REQ_TIMEOUT, request_timeout, + session); + return 0; + +failed: + g_free(req->msg); + g_free(req); + return err; +} + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, void *buffer, int size) +{ + struct pending_req *req; + + req = g_new0(struct pending_req, 1); + req->msg = g_malloc(size); + memcpy(req->msg, buffer, size); + req->msg_size = size; + req->stream = stream; + + return send_req(session, priority, req); +} + +static gboolean avdtp_discover_resp(struct avdtp *session, + struct discover_resp *resp, int size) +{ + int sep_count, i, isize = sizeof(struct seid_info); + + sep_count = (size - sizeof(struct avdtp_header)) / isize; + + for (i = 0; i < sep_count; i++) { + struct avdtp_remote_sep *sep; + struct seid_req req; + int ret; + + /* Skip SEP's which are in use */ + if (resp->seps[i].inuse) + continue; + + sep = find_remote_sep(session->seps, resp->seps[i].seid); + if (!sep) { + sep = g_new0(struct avdtp_remote_sep, 1); + session->seps = g_slist_append(session->seps, sep); + } + else if (sep && sep->stream) + continue; + + sep->seid = resp->seps[i].seid; + sep->type = resp->seps[i].type; + sep->media_type = resp->seps[i].media_type; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_GET_CAPABILITIES); + req.acp_seid = sep->seid; + + ret = send_request(session, TRUE, NULL, &req, sizeof(req)); + if (ret < 0) { + finalize_discovery(session, ret); + break; + } + } + + return TRUE; +} + +static gboolean avdtp_get_capabilities_resp(struct avdtp *session, + struct getcap_resp *resp, + int size) +{ + int processed; + struct avdtp_remote_sep *sep; + unsigned char *ptr; + uint8_t seid; + + /* Check for minimum required packet size includes: + * 1. getcap resp header + * 2. media transport capability (2 bytes) + * 3. media codec capability type + length (2 bytes) + * 4. the actual media codec elements + * */ + if (size < (sizeof(struct getcap_resp) + 4 + + sizeof(struct avdtp_media_codec_capability))) { + error("Too short getcap resp packet"); + return FALSE; + } + + seid = ((struct seid_req *) session->req->msg)->acp_seid; + + sep = find_remote_sep(session->seps, seid); + + if (sep->caps) { + g_slist_foreach(sep->caps, (GFunc) g_free, NULL); + g_slist_free(sep->caps); + sep->caps = NULL; + sep->codec = NULL; + } + + ptr = resp->caps; + processed = sizeof(struct getcap_resp); + + while (processed + 2 < size) { + struct avdtp_service_capability *cap; + uint8_t length, category; + + category = ptr[0]; + length = ptr[1]; + + if (processed + 2 + length > size) { + error("Invalid capability data in getcap resp"); + return FALSE; + } + + cap = g_malloc(sizeof(struct avdtp_service_capability) + + length); + memcpy(cap, ptr, 2 + length); + + processed += 2 + length; + ptr += 2 + length; + + sep->caps = g_slist_append(sep->caps, cap); + + if (category == AVDTP_MEDIA_CODEC && + length >= + sizeof(struct avdtp_media_codec_capability)) + sep->codec = cap; + + } + + return TRUE; +} + +static gboolean avdtp_set_configuration_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *resp, + int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(sep, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + + return TRUE; +} + +static gboolean avdtp_reconfigure_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *resp, int size) +{ + return TRUE; +} + +static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (l2cap_connect(session) < 0) + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); + + session->pending_open = stream; + + return TRUE; +} + +static gboolean avdtp_start_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->start) + sep->cfm->start(sep, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + + return TRUE; +} + +static gboolean avdtp_close_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + close(stream->sock); + stream->sock = -1; + + return TRUE; +} + +static gboolean avdtp_suspend_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct stream_pause_resp *resp, + int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(sep, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + + return TRUE; +} + +static gboolean avdtp_abort_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(sep, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); + + return TRUE; +} + +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *header, int size) +{ + struct avdtp_header *next; + + if (session->prio_queue) + next = ((struct pending_req *) + (session->prio_queue->data))->msg; + else if (session->req_queue) + next = ((struct pending_req *) + (session->req_queue->data))->msg; + else + next = NULL; + + switch (header->signal_id) { + case AVDTP_DISCOVER: + debug("DISCOVER request succeeded"); + return avdtp_discover_resp(session, (void *) header, size); + case AVDTP_GET_CAPABILITIES: + debug("GET_CAPABILITIES request succeeded"); + if (!avdtp_get_capabilities_resp(session, (void *) header, + size)) + return FALSE; + if (!(next && next->signal_id == AVDTP_GET_CAPABILITIES)) + finalize_discovery(session, 0); + return TRUE; + case AVDTP_SET_CONFIGURATION: + debug("SET_CONFIGURATION request succeeded"); + return avdtp_set_configuration_resp(session, stream, + (void *) header, size); + case AVDTP_RECONFIGURE: + debug("RECONFIGURE request succeeded"); + return avdtp_reconfigure_resp(session, stream, (void *) header, + size); + case AVDTP_OPEN: + debug("OPEN request succeeded"); + return avdtp_open_resp(session, stream, (void *) header, size); + case AVDTP_SUSPEND: + debug("SUSPEND request succeeded"); + return avdtp_suspend_resp(session, stream, (void *) header, + size); + case AVDTP_START: + debug("START request succeeded"); + return avdtp_start_resp(session, stream, (void *) header, + size); + case AVDTP_CLOSE: + debug("CLOSE request succeeded"); + return avdtp_close_resp(session, stream, (void *) header, + size); + case AVDTP_ABORT: + debug("ABORT request succeeded"); + return avdtp_abort_resp(session, stream, (void *) header, + size); + } + + error("Unknown signal id in accept response: %u", header->signal_id); + + return TRUE; +} + +static gboolean seid_rej_to_err(struct seid_rej *rej, int size, + struct avdtp_error *err) +{ + if (size < sizeof(struct seid_rej)) { + error("Too small packet for seid_rej"); + return FALSE; + } + + avdtp_error_init(err, AVDTP_ERROR_ERROR_CODE, rej->error); + + return TRUE; +} + +static gboolean conf_rej_to_err(struct conf_rej *rej, int size, + struct avdtp_error *err, uint8_t *category) +{ + if (size < sizeof(struct conf_rej)) { + error("Too small packet for conf_rej"); + return FALSE; + } + + avdtp_error_init(err, AVDTP_ERROR_ERROR_CODE, rej->error); + + if (category) + *category = rej->category; + + return TRUE; +} + +static gboolean stream_rej_to_err(struct stream_rej *rej, int size, + struct avdtp_error *err, uint8_t *acp_seid) +{ + if (size < sizeof(struct conf_rej)) { + error("Too small packet for stream_rej"); + return FALSE; + } + + avdtp_error_init(err, AVDTP_ERROR_ERROR_CODE, rej->error_code); + + if (acp_seid) + *acp_seid = rej->acp_seid; + + return TRUE; +} + +static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, + struct avdtp_header *header, int size) +{ + struct avdtp_error err; + uint8_t acp_seid, category; + + switch (header->signal_id) { + case AVDTP_DISCOVER: + if (!seid_rej_to_err((void *) header, size, &err)) + return FALSE; + error("DISCOVER request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_GET_CAPABILITIES: + if (!seid_rej_to_err((void *) header, size, &err)) + return FALSE; + error("GET_CAPABILITIES request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_OPEN: + if (!seid_rej_to_err((void *) header, size, &err)) + return FALSE; + error("OPEN request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_SET_CONFIGURATION: + if (!conf_rej_to_err((void *) header, size, &err, &category)) + return FALSE; + error("SET_CONFIGURATION request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_RECONFIGURE: + if (!conf_rej_to_err((void *) header, size, &err, &category)) + return FALSE; + error("RECONFIGURE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_START: + if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + return FALSE; + error("START request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_SUSPEND: + if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + return FALSE; + error("SUSPEND request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_CLOSE: + if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + return FALSE; + error("CLOSE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + case AVDTP_ABORT: + if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) + return FALSE; + error("ABORT request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + return TRUE; + default: + error("Unknown reject response signal id: %u", + header->signal_id); + return TRUE; + } +} + +static struct avdtp *find_session(bdaddr_t *src, bdaddr_t *dst) +{ + GSList *l; + + for (l = sessions; l != NULL; l = g_slist_next(l)) { + struct avdtp *s = l->data; + + if (bacmp(src, &s->src) || bacmp(dst, &s->dst)) + continue; + + return s; + } + + return NULL; +} + +struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) +{ + struct avdtp *session; + + assert(src != NULL); + assert(dst != NULL); + + session = find_session(src, dst); + if (session) + return avdtp_ref(session); + + session = g_new0(struct avdtp, 1); + + session->sock = -1; + bacpy(&session->src, src); + bacpy(&session->dst, dst); + session->ref = 1; + session->state = AVDTP_SESSION_STATE_DISCONNECTED; + + sessions = g_slist_append(sessions, session); + + return avdtp_ref(session); +} + +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *mtu, GSList **caps) +{ + if (stream->sock < 0) + return FALSE; + + if (sock) + *sock = stream->sock; + + if (mtu) + *mtu = stream->mtu; + + if (caps) + *caps = stream->caps; + + return TRUE; +} + +static int process_queue(struct avdtp *session) +{ + GSList **queue, *l; + struct pending_req *req; + + if (session->req) + return 0; + + if (session->prio_queue) + queue = &session->prio_queue; + else + queue = &session->req_queue; + + if (!*queue) + return 0; + + l = *queue; + req = l->data; + + *queue = g_slist_remove(*queue, req); + + return send_req(session, FALSE, req); +} + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) +{ + return sep->codec; +} + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + void *data, int length) +{ + struct avdtp_service_capability *cap; + + cap = g_malloc(sizeof(struct avdtp_service_capability) + length); + cap->category = category; + cap->length = length; + memcpy(cap->data, data, length); + + return cap; +} + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) +{ + struct discover_req req; + int ret; + + if (session->discov_cb) + return -EBUSY; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_DISCOVER); + + ret = send_request(session, FALSE, NULL, &req, sizeof(req)); + if (ret == 0) { + session->discov_cb = cb; + session->user_data = user_data; + } + + return ret; +} + +int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, + uint8_t codec, struct avdtp_local_sep **lsep, + struct avdtp_remote_sep **rsep) +{ + GSList *l; + uint8_t int_type; + + int_type = acp_type == AVDTP_SEP_TYPE_SINK ? + AVDTP_SEP_TYPE_SOURCE : AVDTP_SEP_TYPE_SINK; + + *lsep = find_local_sep(int_type, media_type, codec); + if (!*lsep) + return -EINVAL; + + for (l = session->seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_data; + + if (sep->type != acp_type) + continue; + + if (sep->media_type != media_type) + continue; + + if (!sep->codec) + continue; + + cap = sep->codec; + codec_data = (void *) cap->data; + + if (codec_data->media_codec_type != codec) + continue; + + if (!sep->stream) { + *rsep = sep; + return 0; + } + } + + return -EINVAL; +} + +void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data) +{ + stream->cb = cb; + stream->user_data = data; +} + +int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (session->state < AVDTP_SESSION_STATE_CONNECTED) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_GET_CONFIGURATION); + req.acp_seid = stream->rsep->seid; + + return send_request(session, FALSE, stream, &req, sizeof(req)); +} + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream) +{ + struct setconf_req *req; + struct avdtp_stream *new_stream; + unsigned char *ptr; + int ret, caps_len; + struct avdtp_service_capability *cap; + GSList *l; + + if (session->state != AVDTP_SESSION_STATE_CONNECTED) + return -ENOTCONN; + + if (!(lsep && rsep)) + return -EINVAL; + + new_stream = g_new0(struct avdtp_stream, 1); + + new_stream->session = session; + new_stream->lsep = lsep; + new_stream->rsep = rsep; + new_stream->caps = caps; + + /* Calculate total size of request */ + for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { + cap = l->data; + caps_len += cap->length + 2; + } + + req = g_malloc0(sizeof(struct setconf_req) + caps_len); + + init_request(&req->header, AVDTP_SET_CONFIGURATION); + req->acp_seid = lsep->info.seid; + req->int_seid = rsep->seid; + + /* Copy the capabilities into the request */ + for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { + cap = l->data; + memcpy(ptr, cap, cap->length + 2); + ptr += cap->length + 2; + } + + ret = send_request(session, FALSE, new_stream, req, + sizeof(struct setconf_req) + caps_len); + if (ret < 0) + stream_free(new_stream); + else { + lsep->info.inuse = 1; + lsep->stream = new_stream; + rsep->stream = new_stream; + session->streams = g_slist_append(session->streams, new_stream); + if (stream) + *stream = new_stream; + } + + g_free(req); + + return ret; +} + +int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_OPEN) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_GET_CONFIGURATION); + req.acp_seid = stream->rsep->seid; + + return send_request(session, FALSE, NULL, &req, sizeof(req)); +} + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state > AVDTP_STATE_CONFIGURED) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_OPEN); + req.acp_seid = stream->rsep->seid; + + return send_request(session, FALSE, stream, &req, sizeof(req)); +} + +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_OPEN) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_START); + req.acp_seid = stream->rsep->seid; + + return send_request(session, FALSE, stream, &req, sizeof(req)); +} + +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state < AVDTP_STATE_OPEN) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_CLOSE); + req.acp_seid = stream->rsep->seid; + + ret = send_request(session, FALSE, stream, &req, sizeof(req)); + if (ret == 0) + stream->close_int = TRUE; + + return ret; +} + +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state <= AVDTP_STATE_OPEN) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_SUSPEND); + req.acp_seid = stream->rsep->seid; + + ret = send_request(session, FALSE, stream, &req, sizeof(req)); + if (ret == 0) + avdtp_sep_set_state(session, stream->lsep, + AVDTP_STATE_OPEN); + + return ret; +} + +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state <= AVDTP_STATE_OPEN) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + init_request(&req.header, AVDTP_ABORT); + req.acp_seid = stream->rsep->seid; + + ret = send_request(session, FALSE, stream, &req, sizeof(req)); + if (ret == 0) + avdtp_sep_set_state(session, stream->lsep, + AVDTP_STATE_ABORTING); + + return 0; +} + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm) +{ + struct avdtp_local_sep *sep; + + if (free_seid > MAX_SEID) + return NULL; + + sep = g_new0(struct avdtp_local_sep, 1); + + sep->state = AVDTP_STATE_IDLE; + sep->info.seid = free_seid++; + sep->info.type = type; + sep->info.media_type = media_type; + sep->ind = ind; + sep->cfm = cfm; + + local_seps = g_slist_append(local_seps, sep); + + return sep; +} + +int avdtp_unregister_sep(struct avdtp_local_sep *sep) +{ + if (!sep) + return -EINVAL; + + if (sep->info.inuse) + return -EBUSY; + + local_seps = g_slist_remove(local_seps, sep); + + g_free(sep); + + return 0; +} + +static gboolean avdtp_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 avdtp *session; + GIOChannel *cli_io; + char address[18]; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on AVDTP 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("AVDTP accept: %s (%d)", strerror(errno), errno); + return TRUE; + } + + bacpy(&dst, &addr.l2_bdaddr); + + ba2str(&dst, address); + debug("AVDTP: incoming connect from %s", address); + + size = sizeof(struct sockaddr_l2); + if (getsockname(srv_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 = avdtp_get(&src, &dst); + + if (session->pending_open && session->pending_open->open_acp) { + handle_transport_connect(session, cli_sk, l2o.imtu); + return TRUE; + } + + if (session->sock >= 0) { + error("Refusing unexpected connect from %s", address); + close(cli_sk); + return TRUE; + } + + if (session->ref == 1) + set_disconnect_timer(session); + + session->mtu = l2o.imtu; + session->buf = g_malloc0(session->mtu); + session->sock = cli_sk; + session->state = AVDTP_SESSION_STATE_CONNECTED; + + cli_io = g_io_channel_unix_new(session->sock); + session->io = g_io_add_watch(cli_io, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) session_cb, session); + g_io_channel_unref(cli_io); + + return TRUE; +} + +static GIOChannel *avdtp_server_socket(void) +{ + int sock, lm; + struct sockaddr_l2 addr; + GIOChannel *io; + + sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (sock < 0) { + error("AVDTP 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("AVDTP 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(AVDTP_PSM); + + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("AVDTP server bind: %s", strerror(errno), errno); + close(sock); + return NULL; + } + + if (listen(sock, 4) < 0) { + error("AVDTP 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; +} + +const char *avdtp_strerror(struct avdtp_error *err) +{ + if (err->type == AVDTP_ERROR_ERRNO) + return strerror(err->err.posix_errno); + + switch(err->err.error_code) { + case AVDTP_BAD_HEADER_FORMAT: + return "Bad Header Format"; + case AVDTP_BAD_LENGTH: + return "Bad Packet Lenght"; + case AVDTP_BAD_ACP_SEID: + return "Bad Acceptor SEID"; + case AVDTP_SEP_IN_USE: + return "Stream End Point in Use"; + case AVDTP_SEP_NOT_IN_USE: + return "Stream End Point Not in Use"; + case AVDTP_BAD_SERV_CATEGORY: + return "Bad Service Category"; + case AVDTP_BAD_PAYLOAD_FORMAT: + return "Bad Payload format"; + case AVDTP_NOT_SUPPORTED_COMMAND: + return "Command Not Supported"; + case AVDTP_INVALID_CAPABILITIES: + return "Invalid Capabilities"; + case AVDTP_BAD_RECOVERY_TYPE: + return "Bad Recovery Type"; + case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: + return "Bad Media Transport Format"; + case AVDTP_BAD_RECOVERY_FORMAT: + return "Bad Recovery Format"; + case AVDTP_BAD_ROHC_FORMAT: + return "Bad Header Compression Format"; + case AVDTP_BAD_CP_FORMAT: + return "Bad Content Protetion Format"; + case AVDTP_BAD_MULTIPLEXING_FORMAT: + return "Bad Multiplexing Format"; + case AVDTP_UNSUPPORTED_CONFIGURATION: + return "Configuration not supported"; + case AVDTP_BAD_STATE: + return "Bad State"; + default: + return "Unknow error"; + } +} + +int avdtp_init(void) +{ + if (avdtp_server) + return 0; + + avdtp_server = avdtp_server_socket(); + if (!avdtp_server) + return -1; + + g_io_add_watch(avdtp_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) avdtp_server_cb, NULL); + + return 0; +} + +void avdtp_exit(void) +{ + if (!avdtp_server) + return; + + g_io_channel_close(avdtp_server); + g_io_channel_unref(avdtp_server); + avdtp_server = NULL; +} diff --git a/audio/avdtp.h b/audio/avdtp.h index 153a1731..b4af9eb3 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -20,4 +20,181 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AVDTP_H__ +#define __AVDTP_H__ +#include +#include + +struct avdtp; +struct avdtp_stream; +struct avdtp_local_sep; +struct avdtp_remote_sep; +struct avdtp_error; + +/* SEP capability categories */ +#define AVDTP_MEDIA_TRANSPORT 0x01 +#define AVDTP_REPORTING 0x02 +#define AVDTP_RECOVERY 0x03 +#define AVDTP_CONTENT_PROTECTION 0x04 +#define AVDTP_HEADER_COMPRESSION 0x05 +#define AVDTP_MULTIPLEXING 0x06 +#define AVDTP_MEDIA_CODEC 0x07 + +/* AVDTP error definitions */ +#define AVDTP_BAD_HEADER_FORMAT 0x01 +#define AVDTP_BAD_LENGTH 0x11 +#define AVDTP_BAD_ACP_SEID 0x12 +#define AVDTP_SEP_IN_USE 0x13 +#define AVDTP_SEP_NOT_IN_USE 0x14 +#define AVDTP_BAD_SERV_CATEGORY 0x17 +#define AVDTP_BAD_PAYLOAD_FORMAT 0x18 +#define AVDTP_NOT_SUPPORTED_COMMAND 0x19 +#define AVDTP_INVALID_CAPABILITIES 0x1A +#define AVDTP_BAD_RECOVERY_TYPE 0x22 +#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define AVDTP_BAD_RECOVERY_FORMAT 0x25 +#define AVDTP_BAD_ROHC_FORMAT 0x26 +#define AVDTP_BAD_CP_FORMAT 0x27 +#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 +#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 +#define AVDTP_BAD_STATE 0x31 + +/* SEP types definitions */ +#define AVDTP_SEP_TYPE_SOURCE 0x00 +#define AVDTP_SEP_TYPE_SINK 0x01 + +/* Media types definitions */ +#define AVDTP_MEDIA_TYPE_AUDIO 0x00 +#define AVDTP_MEDIA_TYPE_VIDEO 0x01 +#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 + +typedef enum { + AVDTP_STATE_IDLE, + AVDTP_STATE_CONFIGURED, + AVDTP_STATE_OPEN, + AVDTP_STATE_STREAMING, + AVDTP_STATE_CLOSING, + AVDTP_STATE_ABORTING, +} avdtp_state_t; + +struct avdtp_service_capability { + uint8_t category; + uint8_t length; + uint8_t data[0]; +} __attribute__ ((packed)); + +struct avdtp_media_codec_capability { + uint8_t rfa0:4; + uint8_t media_type:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data); + +/* Callbacks for when a reply is received to a command that we sent */ +struct avdtp_sep_cfm { + void (*set_configuration) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*get_configuration) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*open) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*start) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*suspend) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*close) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*abort) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream); + void (*reconfigure) (struct avdtp_local_sep *lsep); +}; + +/* Callbacks for indicating when we received a new command. The return value + * indicates whether the command should be rejected or accepted */ +struct avdtp_sep_ind { + gboolean (*get_capability) (struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err); + gboolean (*set_configuration) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + uint8_t int_seid, GSList *caps, + uint8_t *err); + gboolean (*get_configuration) (struct avdtp_local_sep *lsep, + uint8_t *err); + gboolean (*open) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err); + gboolean (*start) (struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + uint8_t *err); + gboolean (*suspend) (struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err); + gboolean (*close) (struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err); + gboolean (*abort) (struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err); + gboolean (*reconfigure) (struct avdtp_local_sep *lsep, uint8_t *err); +}; + +typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, + int err, void *user_data); + +struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst); + +void avdtp_unref(struct avdtp *session); +struct avdtp *avdtp_ref(struct avdtp *session); + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + void *data, int size); + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data); + +void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data); + +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *mtu, GSList **caps); + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream); + +int avdtp_get_configuration(struct avdtp *session, + struct avdtp_stream *stream); + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm); + +/* Find a matching pair of local and remote SEP ID's */ +int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media, + uint8_t codec, struct avdtp_local_sep **lsep, + struct avdtp_remote_sep **rsep); + +int avdtp_unregister_sep(struct avdtp_local_sep *sep); + +const char *avdtp_strerror(struct avdtp_error *err); + +int avdtp_init(void); +void avdtp_exit(void); + +#endif diff --git a/audio/device.c b/audio/device.c index 785d0115..af7cca80 100644 --- a/audio/device.c +++ b/audio/device.c @@ -100,21 +100,24 @@ static DBusMethodVTable device_methods[] = { { NULL, NULL, NULL, NULL } }; -static void device_free(struct device *device) +static void device_free(struct device *dev) { - if (device->headset) - headset_free(device); + if (dev->headset) + headset_free(dev); - if (device->conn) - dbus_connection_unref(device->conn); + if (dev->sink) + sink_free(dev); - if (device->adapter_path) - g_free(device->adapter_path); + if (dev->conn) + dbus_connection_unref(dev->conn); - if (device->path) - g_free(device->path); + if (dev->adapter_path) + g_free(dev->adapter_path); - g_free(device); + if (dev->path) + g_free(dev->path); + + g_free(dev); } static void device_unregister(DBusConnection *conn, void *data) @@ -129,7 +132,7 @@ static void device_unregister(DBusConnection *conn, void *data) struct device *device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) { - struct device *device; + struct device *dev; bdaddr_t src; int dev_id; @@ -141,12 +144,12 @@ struct device *device_register(DBusConnection *conn, if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return NULL; - device = g_new0(struct device, 1); + dev = g_new0(struct device, 1); - if (!dbus_connection_create_object_path(conn, path, device, + if (!dbus_connection_create_object_path(conn, path, dev, device_unregister)) { error("D-Bus failed to register %s path", path); - device_free(device); + device_free(dev); return NULL; } @@ -158,58 +161,69 @@ struct device *device_register(DBusConnection *conn, return NULL; } - device->path = g_strdup(path); - bacpy(&device->dst, bda); - bacpy(&device->src, &src); - device->conn = dbus_connection_ref(conn); - device->adapter_path = g_malloc0(16); - snprintf(device->adapter_path, 16, "/org/bluez/hci%d", dev_id); + dev->path = g_strdup(path); + bacpy(&dev->dst, bda); + bacpy(&dev->src, &src); + dev->conn = dbus_connection_ref(conn); + dev->adapter_path = g_malloc0(16); + snprintf(dev->adapter_path, 16, "/org/bluez/hci%d", dev_id); - return device; + return dev; } -int device_store(struct device *device, gboolean is_default) +int device_store(struct device *dev, gboolean is_default) { char value[64]; char filename[PATH_MAX + 1]; char src_addr[18], dst_addr[18]; + int offset = 0; - if (!device->path) + if (!dev->path) return -EINVAL; - ba2str(&device->dst, dst_addr); - ba2str(&device->src, src_addr); + ba2str(&dev->dst, dst_addr); + ba2str(&dev->src, src_addr); create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (is_default) textfile_put(filename, "default", dst_addr); - if (device->headset) - snprintf(value, 64, "headset"); - else if (device->gateway) - snprintf(value, 64, "gateway"); - else if (device->sink) - snprintf(value, 64, "sink"); - else if (device->source) - snprintf(value, 64, "source"); - else if (device->control) - snprintf(value, 64, "control"); - else - snprintf(value, 64, "target"); + if (dev->headset) { + snprintf(value, 64, "headset "); + offset += strlen("headset "); + } + if (dev->gateway) { + snprintf(value + offset, 64 - offset, "gateway "); + offset += strlen("gateway "); + } + if (dev->sink) { + snprintf(value + offset, 64 - offset, "sink "); + offset += strlen("sink "); + } + if (dev->source) { + snprintf(value + offset, 64 - offset, "source "); + offset += strlen("source "); + } + if (dev->control) { + snprintf(value + offset, 64 - offset, "control "); + offset += strlen("control "); + } + if (dev->target) + snprintf(value + offset, 64 - offset, "target"); return textfile_put(filename, dst_addr, value); } -void device_finish_sdp_transaction(struct device *device) +void device_finish_sdp_transaction(struct device *dev) { char address[18], *addr_ptr = address; DBusMessage *msg, *reply; DBusError derr; - ba2str(&device->dst, address); + ba2str(&dev->dst, address); - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + msg = dbus_message_new_method_call("org.bluez", dev->adapter_path, "org.bluez.Adapter", "FinishRemoteServiceTransaction"); if (!msg) { @@ -221,7 +235,7 @@ void device_finish_sdp_transaction(struct device *device) DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(device->conn, + reply = dbus_connection_send_with_reply_and_block(dev->conn, msg, -1, &derr); dbus_message_unref(msg); @@ -236,3 +250,114 @@ void device_finish_sdp_transaction(struct device *device) dbus_message_unref(reply); } + +int device_get_config(struct device *dev, int sock, struct ipc_packet *req, + int pkt_len, struct ipc_data_cfg **rsp) +{ + if (dev->sink && sink_is_active(dev)) + return sink_get_config(dev, sock, req, pkt_len, rsp); + else if (dev->headset && headset_is_active(dev)) + return headset_get_config(dev, sock, req, pkt_len, rsp); + else if (dev->sink) + return sink_get_config(dev, sock, req, pkt_len, rsp); + else if (dev->headset) + return headset_get_config(dev, sock, req, pkt_len, rsp); + + return -EINVAL; +} + +static avdtp_state_t ipc_to_avdtp_state(uint8_t ipc_state) +{ + switch (ipc_state) { + case STATE_DISCONNECTED: + return AVDTP_STATE_IDLE; + case STATE_CONNECTING: + return AVDTP_STATE_CONFIGURED; + case STATE_CONNECTED: + return AVDTP_STATE_OPEN; + case STATE_STREAM_STARTING: + case STATE_STREAMING: + return AVDTP_STATE_STREAMING; + default: + error("Unknown ipc state"); + return AVDTP_STATE_IDLE; + } +} + +static headset_state_t ipc_to_hs_state(uint8_t ipc_state) +{ + switch (ipc_state) { + case STATE_DISCONNECTED: + return HEADSET_STATE_DISCONNECTED; + case STATE_CONNECTING: + return HEADSET_STATE_CONNECT_IN_PROGRESS; + case STATE_CONNECTED: + return HEADSET_STATE_CONNECTED; + case STATE_STREAM_STARTING: + return HEADSET_STATE_PLAY_IN_PROGRESS; + case STATE_STREAMING: + return HEADSET_STATE_PLAYING; + default: + error("Unknown ipc state"); + return HEADSET_STATE_DISCONNECTED; + } +} + +void device_set_state(struct device *dev, uint8_t state) +{ + if (dev->sink && sink_is_active(dev)) + sink_set_state(dev, ipc_to_avdtp_state(state)); + else if (dev->headset && headset_is_active(dev)) + headset_set_state(dev, ipc_to_hs_state(state)); +} + +static uint8_t avdtp_to_ipc_state(avdtp_state_t state) +{ + switch (state) { + case AVDTP_STATE_IDLE: + return STATE_DISCONNECTED; + case AVDTP_STATE_CONFIGURED: + return STATE_CONNECTING; + case AVDTP_STATE_OPEN: + return STATE_CONNECTED; + case AVDTP_STATE_STREAMING: + return STATE_STREAMING; + default: + error("Unknown avdt state"); + return AVDTP_STATE_IDLE; + } +} + +static uint8_t hs_to_ipc_state(headset_state_t state) +{ + switch (state) { + case HEADSET_STATE_DISCONNECTED: + return STATE_DISCONNECTED; + case HEADSET_STATE_CONNECT_IN_PROGRESS: + return STATE_CONNECTING; + case HEADSET_STATE_CONNECTED: + return STATE_CONNECTED; + case HEADSET_STATE_PLAY_IN_PROGRESS: + return STATE_STREAMING; + default: + error("Unknown headset state"); + return AVDTP_STATE_IDLE; + } +} + +uint8_t device_get_state(struct device *dev) +{ + avdtp_state_t sink_state; + headset_state_t hs_state; + + if (dev->sink && sink_is_active(dev)) { + sink_state = sink_get_state(dev); + return avdtp_to_ipc_state(sink_state); + } + else if (dev->headset && headset_is_active(dev)) { + hs_state = headset_get_state(dev); + return hs_to_ipc_state(hs_state); + } + + return STATE_DISCONNECTED; +} diff --git a/audio/device.h b/audio/device.h index 71e1053f..62c13e9e 100644 --- a/audio/device.h +++ b/audio/device.h @@ -75,3 +75,10 @@ struct device *device_register(DBusConnection *conn, int device_store(struct device *device, gboolean is_default); void device_finish_sdp_transaction(struct device *device); + +int device_get_config(struct device *dev, int sock, struct ipc_packet *req, + int pkt_len, struct ipc_data_cfg **rsp); + +void device_set_state(struct device *dev, uint8_t state); + +uint8_t device_get_state(struct device *dev); diff --git a/audio/headset.c b/audio/headset.c index 9dccc5ad..2bc8583b 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -53,6 +53,7 @@ #include "logging.h" #include "manager.h" #include "error.h" +#include "unix.h" #define RING_INTERVAL 3000 @@ -68,6 +69,7 @@ struct pending_connect { DBusMessage *msg; GIOChannel *io; struct ipc_packet *pkt; + int pkt_len; guint io_id; int sock; int err; @@ -102,7 +104,7 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c); static void pending_connect_free(struct pending_connect *c) { if (c->pkt) - unix_send_cfg(c->sock, c->pkt); + g_free(c->pkt); if (c->io) { g_io_channel_close(c->io); g_io_channel_unref(c->io); @@ -339,8 +341,19 @@ static void pending_connect_ok(struct pending_connect *c, struct device *dev) if (reply) send_message_and_unref(dev->conn, reply); } - else if (c->pkt) - headset_get_config(dev, c->sock, c->pkt); + else if (c->pkt) { + struct ipc_data_cfg *rsp; + int ret; + + ret = headset_get_config(dev, c->sock, c->pkt, + c->pkt_len, &rsp); + if (ret == 0) { + unix_send_cfg(c->sock, rsp); + g_free(rsp); + } + else + unix_send_cfg(c->sock, NULL); + } pending_connect_free(c); } @@ -349,6 +362,8 @@ static void pending_connect_failed(struct pending_connect *c, struct device *dev { if (c->msg) err_connect_failed(dev->conn, c->msg, strerror(c->err)); + if (c->pkt) + unix_send_cfg(c->sock, NULL); pending_connect_free(c); } @@ -1358,7 +1373,8 @@ register_iface: void headset_free(void *device) { - struct headset *hs = ((struct device *) device)->headset; + struct device *dev = device; + struct headset *hs = dev->headset; if (hs->sco) { g_io_channel_close(hs->sco); @@ -1371,54 +1387,54 @@ void headset_free(void *device) } g_free(hs); - hs = NULL; + dev->headset = NULL; } -int headset_get_config(void *device, int sock, struct ipc_packet *pkt) +int headset_get_config(void *device, int sock, struct ipc_packet *pkt, + int pkt_len, struct ipc_data_cfg **cfg) { struct headset *hs = ((struct device *) device)->headset; - struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; int err = EINVAL; struct pending_connect *c; + struct ipc_data_cfg *rsp; + + if (hs->rfcomm && hs->sco) + goto proceed; - if (hs->rfcomm == NULL) { - c = g_try_new0(struct pending_connect, 1); - if (c == NULL) - goto error; - c->sock = sock; - c->pkt = pkt; + c = g_new0(struct pending_connect, 1); + c->sock = sock; + c->pkt = g_malloc(pkt_len); + memcpy(c->pkt, pkt, pkt_len); + + if (hs->rfcomm == NULL) err = rfcomm_connect(device, c); - if (err < 0) - goto error; - return 0; - } - else if (hs->sco == NULL) { - c = g_try_new0(struct pending_connect, 1); - if (c == NULL) - goto error; - c->sock = sock; - c->pkt = pkt; + else if (hs->sco == NULL) err = sco_connect(device, c); - if (err < 0) - goto error; - return 0; - } + else + goto error; + + if (err < 0) + goto error; - cfg->fd = g_io_channel_unix_get_fd(hs->sco); - cfg->fd_opt = CFG_FD_OPT_READWRITE; - cfg->encoding = 0; - cfg->bitpool = 0; - cfg->channels = 1; - cfg->pkt_len = 48; - cfg->sample_size = 2; - cfg->rate = 8000; + return 1; + +proceed: + *cfg = g_new0(struct ipc_data_cfg, 1); + rsp = *cfg; + rsp->fd = g_io_channel_unix_get_fd(hs->sco); + rsp->fd_opt = CFG_FD_OPT_READWRITE; + rsp->codec = CFG_CODEC_NONE; + rsp->channels = 1; + rsp->channel_mode = CFG_CHANNEL_MODE_MONO; + rsp->pkt_len = 48; + rsp->sample_size = 2; + rsp->rate = 8000; return 0; error: if (c) pending_connect_free(c); - cfg->fd = -1; return -err; } @@ -1547,3 +1563,14 @@ int headset_get_channel(void *device) return hs->rfcomm_ch; } + +gboolean headset_is_active(void *device) +{ + struct device *dev = device; + struct headset *hs = dev->headset; + + if (hs->state != HEADSET_STATE_DISCONNECTED) + return TRUE; + + return FALSE; +} diff --git a/audio/headset.h b/audio/headset.h index d3fd86d9..530bdea8 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,12 +20,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AUDIO_HEADSET_H__ +#define __AUDIO_HEADSET_H__ + #include #include #include -#include "unix.h" +#include "ipc.h" #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" @@ -40,11 +43,11 @@ typedef enum { } headset_event_t; typedef enum { - HEADSET_STATE_DISCONNECTED = STATE_DISCONNECTED, - HEADSET_STATE_CONNECT_IN_PROGRESS = STATE_CONNECTING, - HEADSET_STATE_CONNECTED = STATE_CONNECTED, - HEADSET_STATE_PLAY_IN_PROGRESS = STATE_STREAM_STARTING, - HEADSET_STATE_PLAYING = STATE_STREAMING, + HEADSET_STATE_DISCONNECTED, + HEADSET_STATE_CONNECT_IN_PROGRESS, + HEADSET_STATE_CONNECTED, + HEADSET_STATE_PLAY_IN_PROGRESS, + HEADSET_STATE_PLAYING } headset_state_t; typedef enum { @@ -55,13 +58,14 @@ typedef enum { struct headset; struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc); + uint16_t svc); void headset_free(void *device); void headset_update(void *device, sdp_record_t *record, uint16_t svc); -int headset_get_config(void *device, int sock, struct ipc_packet *pkt); +int headset_get_config(void *device, int sock, struct ipc_packet *pkt, + int pkt_len, struct ipc_data_cfg **rsp); headset_type_t headset_get_type(void *device); void headset_set_type(void *device, headset_type_t type); @@ -73,3 +77,7 @@ headset_state_t headset_get_state(void *device); void headset_set_state(void *device, headset_state_t state); int headset_get_channel(void *device); + +gboolean headset_is_active(void *device); + +#endif diff --git a/audio/ipc.h b/audio/ipc.h index e56dca24..bd31abbc 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -20,11 +20,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AUDIO_IPC_H__ +#define __AUDIO_IPC_H__ #include #define IPC_TYPE_CONNECT 0x0001 +#define IPC_MTU 32 + #define IPC_SOCKET_NAME "\0/org/bluez/audio" #ifndef UNIX_PATH_MAX @@ -32,22 +36,22 @@ #endif /* Supported roles */ -#define PKT_ROLE_NONE 0 -#define PKT_ROLE_AUTO 1 -#define PKT_ROLE_VOICE 2 -#define PKT_ROLE_HIFI 3 +#define PKT_ROLE_NONE 0 +#define PKT_ROLE_AUTO 1 +#define PKT_ROLE_VOICE 2 +#define PKT_ROLE_HIFI 3 /* Packet types */ -#define PKT_TYPE_CFG_REQ 0 -#define PKT_TYPE_CFG_RSP 1 -#define PKT_TYPE_STATE_REQ 2 -#define PKT_TYPE_STATE_RSP 3 -#define PKT_TYPE_CTL_REQ 4 -#define PKT_TYPE_CTL_RSP 5 -#define PKT_TYPE_CTL_NTFY 6 +#define PKT_TYPE_CFG_REQ 0 +#define PKT_TYPE_CFG_RSP 1 +#define PKT_TYPE_STATE_REQ 2 +#define PKT_TYPE_STATE_RSP 3 +#define PKT_TYPE_CTL_REQ 4 +#define PKT_TYPE_CTL_RSP 5 +#define PKT_TYPE_CTL_NTFY 6 /* Errors codes */ -#define PKT_ERROR_NONE 0 +#define PKT_ERROR_NONE 0 struct ipc_packet { uint8_t id; /* Device id */ @@ -59,52 +63,76 @@ struct ipc_packet { } __attribute__ ((packed)); /* File descriptor options */ -#define CFG_FD_OPT_READ 0 -#define CFG_FD_OPT_WRITE 1 -#define CFG_FD_OPT_READWRITE 2 +#define CFG_FD_OPT_READ 0 +#define CFG_FD_OPT_WRITE 1 +#define CFG_FD_OPT_READWRITE 2 + +/* Audio channel mode */ +#define CFG_CHANNEL_MODE_MONO (1 << 3) +#define CFG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define CFG_CHANNEL_MODE_STEREO (1 << 1) +#define CFG_CHANNEL_MODE_JOINT_STEREO 1 + +/* Codec options */ +#define CFG_CODEC_NONE 0 +#define CFG_CODEC_SBC 1 struct ipc_data_cfg { int fd; /* Stream file descriptor */ uint8_t fd_opt; /* Stream file descriptor options: read, write or readwrite*/ - uint8_t encoding; /* Stream encoding */ - uint8_t bitpool; /* Encoding bitpool */ uint8_t channels; /* Number of audio channel */ - uint8_t pkt_len; /* Stream packet length */ + uint8_t channel_mode; /* Audio channel mode*/ + uint16_t pkt_len; /* Stream packet length */ uint8_t sample_size; /* Sample size in bytes */ uint16_t rate; /* Stream sample rate */ + uint8_t codec; /* Stream codec */ + uint8_t data[0]; /* Codec payload */ +} __attribute__ ((packed)); + +/* SBC codec options */ +#define CODEC_SBC_ALLOCATION_SNR (1 << 1) +#define CODEC_SBC_ALLOCATION_LOUDNESS 1 + +struct ipc_codec_sbc { + uint8_t allocation; + uint8_t subbands; + uint8_t blocks; + uint8_t bitpool; } __attribute__ ((packed)); /* Device status */ -#define STATE_DISCONNECTED 0 -#define STATE_CONNECTING 1 -#define STATE_CONNECTED 2 -#define STATE_STREAM_STARTING 3 -#define STATE_STREAMING 4 +#define STATE_DISCONNECTED 0 +#define STATE_CONNECTING 1 +#define STATE_CONNECTED 2 +#define STATE_STREAM_STARTING 3 +#define STATE_STREAMING 4 struct ipc_data_state { uint8_t state; /* Stream state */ } __attribute__ ((packed)); -#define CTL_MODE_PLAYBACK 0 -#define CTL_MODE_CAPTURE 1 -#define CTL_MODE_GENERAL 2 +#define CTL_MODE_PLAYBACK 0 +#define CTL_MODE_CAPTURE 1 +#define CTL_MODE_GENERAL 2 /* Supported control operations */ -#define CTL_KEY_POWER 0x40 -#define CTL_KEY_VOL_UP 0x41 -#define CTL_KEY_VOL_DOWN 0x42 -#define CTL_KEY_MUTE 0x43 -#define CTL_KEY_PLAY 0x44 -#define CTL_KEY_STOP 0x45 -#define CTL_KEY_PAUSE 0x46 -#define CTL_KEY_RECORD 0x47 -#define CTL_KEY_REWIND 0x48 -#define CTL_KEY_FAST_FORWARD 0x49 -#define CTL_KEY_EJECT 0x4A -#define CTL_KEY_FORWARD 0x4B -#define CTL_KEY_BACKWARD 0x4C +#define CTL_KEY_POWER 0x40 +#define CTL_KEY_VOL_UP 0x41 +#define CTL_KEY_VOL_DOWN 0x42 +#define CTL_KEY_MUTE 0x43 +#define CTL_KEY_PLAY 0x44 +#define CTL_KEY_STOP 0x45 +#define CTL_KEY_PAUSE 0x46 +#define CTL_KEY_RECORD 0x47 +#define CTL_KEY_REWIND 0x48 +#define CTL_KEY_FAST_FORWARD 0x49 +#define CTL_KEY_EJECT 0x4A +#define CTL_KEY_FORWARD 0x4B +#define CTL_KEY_BACKWARD 0x4C struct ipc_data_ctl { uint8_t mode; /* Control Mode */ uint8_t key; /* Control Key */ } __attribute__ ((packed)); + +#endif diff --git a/audio/main.c b/audio/main.c index 4da85a29..72e54133 100644 --- a/audio/main.c +++ b/audio/main.c @@ -35,7 +35,7 @@ #include "dbus.h" #include "logging.h" - +#include "unix.h" #include "manager.h" static gboolean disable_hfp = TRUE; @@ -43,6 +43,15 @@ static gboolean sco_hci = FALSE; static GMainLoop *main_loop = NULL; +static struct enabled_interfaces enabled = { + .headset = TRUE, + .gateway = FALSE, + .sink = TRUE, + .source = FALSE, + .control = FALSE, + .target = FALSE, +}; + static void sig_term(int sig) { g_main_loop_quit(main_loop); @@ -53,7 +62,7 @@ static void read_config(const char *file) GKeyFile *keyfile; GError *err = NULL; gboolean no_hfp; - char *sco_routing; + char *str; keyfile = g_key_file_new(); @@ -64,21 +73,42 @@ static void read_config(const char *file) return; } - sco_routing = g_key_file_get_string(keyfile, "General", + str = g_key_file_get_string(keyfile, "General", "SCORouting", &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); err = NULL; } else { - if (strcmp(sco_routing, "PCM") == 0) + if (strcmp(str, "PCM") == 0) sco_hci = FALSE; - else if (strcmp(sco_routing, "HCI") == 0) + else if (strcmp(str, "HCI") == 0) sco_hci = TRUE; else - error("Invalid Headset Routing value: %s", - sco_routing); - g_free(sco_routing); + error("Invalid Headset Routing value: %s", str); + g_free(str); + } + + str = g_key_file_get_string(keyfile, "General", + "Disabled", &err); + if (err) { + debug("%s: %s", file, err->message); + g_error_free(err); + err = NULL; + } else { + if (strstr(str, "Headset")) + enabled.headset = FALSE; + if (strstr(str, "Gateway")) + enabled.gateway = FALSE; + if (strstr(str, "Sink")) + enabled.sink = FALSE; + if (strstr(str, "Source")) + enabled.source = FALSE; + if (strstr(str, "Control")) + enabled.control = FALSE; + if (strstr(str, "Target")) + enabled.target = FALSE; + g_free(str); } no_hfp = g_key_file_get_boolean(keyfile, "Headset", @@ -131,7 +161,7 @@ int main(int argc, char *argv[]) exit(1); } - if (audio_init(conn, disable_hfp, sco_hci) < 0) { + if (audio_init(conn, &enabled, disable_hfp, sco_hci) < 0) { error("Audio init failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index 7e250785..58f19ed0 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -52,6 +52,8 @@ #include "textfile.h" #include "manager.h" #include "error.h" +#include "a2dp.h" +#include "avdtp.h" typedef enum { HEADSET = 1 << 0, @@ -93,6 +95,8 @@ static uint32_t hf_record_id = 0; static GIOChannel *hs_server = NULL; static GIOChannel *hf_server = NULL; +static const struct enabled_interfaces *enabled; + static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, struct audio_sdp_data *data); @@ -190,6 +194,8 @@ static gboolean server_is_enabled(uint16_t svc) case HANDSFREE_SVCLASS_ID: ret = (hf_server != NULL); break; + case AUDIO_SINK_SVCLASS_ID: + return enabled->sink; default: ret = FALSE; break; @@ -233,6 +239,8 @@ static void handle_record(sdp_record_t *record, struct device *device) break; case AUDIO_SINK_SVCLASS_ID: debug("Found Audio Sink"); + if (device->sink == NULL) + device->sink = sink_init(device); break; case AUDIO_SOURCE_SVCLASS_ID: debug("Found Audio Source"); @@ -713,7 +721,7 @@ static gboolean device_supports_interface(struct device *device, return device->source ? TRUE : FALSE; if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) - return device->sink ? TRUE : FALSE; + return device->sink ? TRUE : FALSE; if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) return device->control ? TRUE : FALSE; @@ -1135,9 +1143,10 @@ static void parse_stored_devices(char *key, char *value, void *data) if (!device) return; - if (strncmp(value, "headset", strlen("headset")) == 0) + if (strstr(value, "headset")) device->headset = headset_init(device, NULL, 0); - + if (strstr(value, "sink")) + device->sink = sink_init(device); add_device(device); } @@ -1353,7 +1362,7 @@ static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -static uint32_t add_record(uint8_t channel, sdp_buf_t *buf) +uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf) { DBusMessage *msg, *reply; DBusError derr; @@ -1397,12 +1406,12 @@ static uint32_t add_record(uint8_t channel, sdp_buf_t *buf) dbus_message_unref(reply); - debug("add_record: got record id 0x%x", rec_id); + debug("add_service_record: got record id 0x%x", rec_id); return rec_id; } -static int remove_record(uint32_t rec_id) +int remove_service_record(DBusConnection *conn, uint32_t rec_id) { DBusMessage *msg, *reply; DBusError derr; @@ -1633,11 +1642,14 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -static int server_init(DBusConnection *conn, gboolean no_hfp) +static int headset_server_init(DBusConnection *conn, gboolean no_hfp) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; sdp_buf_t buf; + if (!(enabled->headset || enabled->gateway)) + return 0; + hs_server = server_socket(&chan); if (!hs_server) return -1; @@ -1647,7 +1659,7 @@ static int server_init(DBusConnection *conn, gboolean no_hfp) return -1; } - hs_record_id = add_record(chan, &buf); + hs_record_id = add_service_record(conn, &buf); free(buf.data); if (!hs_record_id) { error("Unable to register HS AG service record"); @@ -1673,7 +1685,7 @@ static int server_init(DBusConnection *conn, gboolean no_hfp) return -1; } - hf_record_id = add_record(chan, &buf); + hf_record_id = add_service_record(conn, &buf); free(buf.data); if (!hf_record_id) { error("Unable to register HS AG service record"); @@ -1691,7 +1703,7 @@ static int server_init(DBusConnection *conn, gboolean no_hfp) static void server_exit(void) { if (hs_record_id) { - remove_record(hs_record_id); + remove_service_record(connection, hs_record_id); hs_record_id = 0; } @@ -1701,7 +1713,7 @@ static void server_exit(void) } if (hf_record_id) { - remove_record(hf_record_id); + remove_service_record(connection, hf_record_id); hf_record_id = 0; } @@ -1711,11 +1723,17 @@ static void server_exit(void) } } -int audio_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci) +int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, + gboolean no_hfp, gboolean sco_hci) { connection = dbus_connection_ref(conn); - if (server_init(conn, no_hfp) < 0) + enabled = enable; + + if (headset_server_init(conn, no_hfp) < 0) + goto failed; + + if (a2dp_init(conn, enable->sink, enable->source) < 0) goto failed; if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, @@ -1754,7 +1772,24 @@ void audio_exit(void) connection = NULL; } -struct device *manager_default_device() +struct device *manager_default_device(void) { return default_dev; } + +struct device *manager_get_connected_device(void) +{ + GSList *l; + + for (l = devices; l != NULL; l = g_slist_next(l)) { + struct device *device = l->data; + + if (device->sink && sink_is_active(device)) + return device; + + if (device->headset && headset_is_active(device)) + return device; + } + + return NULL; +} diff --git a/audio/manager.h b/audio/manager.h index 79fe9090..9fbc4940 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -29,10 +29,25 @@ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" -int audio_init(DBusConnection *conn, gboolean no_hfp, gboolean sco_hci); +struct enabled_interfaces { + gboolean headset; + gboolean gateway; + gboolean sink; + gboolean source; + gboolean control; + gboolean target; +}; + +int audio_init(DBusConnection *conn, struct enabled_interfaces *enabled, + gboolean no_hfp, gboolean sco_hci); void audio_exit(void); +uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); +int remove_service_record(DBusConnection *conn, uint32_t rec_id); + struct device *manager_device_connected(bdaddr_t *bda); struct device *manager_default_device(); + +struct device *manager_get_connected_device(void); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 92d0383c..3f428ecd 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -27,6 +27,9 @@ #include #include +#include + +#include #include #include @@ -35,6 +38,11 @@ #include #include "ipc.h" +#include "sbc.h" + +/*#define ENABLE_DEBUG */ + +#define BUFFER_SIZE 1024 #ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) @@ -50,15 +58,65 @@ #define SCO_RXBUFS 0x04 #endif +struct rtp_header { + uint8_t cc:4; + uint8_t x:1; + uint8_t p:1; + uint8_t v:2; + + uint8_t pt:7; + uint8_t m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t frame_count:4; + uint8_t rfa0:1; + uint8_t is_last_fragment:1; + uint8_t is_first_fragment:1; + uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +struct bluetooth_a2dp { + sbc_t sbc; /* Codec data */ + int samples; /* Number of encoded samples */ + time_t timestamp; /* Codec samples timestamp */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + int count; /* Codec transfer buffer counter */ + + int nsamples; /* Cumulative number of codec samples */ + struct timeval ntimestamp; /* Cumulative timeval */ + uint16_t seq_num; /* */ + int frame_count; /* */ + + int bandwithcount; + struct timeval bandwithtimestamp; +}; + struct bluetooth_data { snd_pcm_ioplug_t io; snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ int sock; /* Daemon unix socket */ - uint8_t *buffer; /* Transfer buffer */ - uint8_t count; /* Transfer buffer counter */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + int count; /* Transfer buffer counter */ + struct bluetooth_a2dp a2dp; /* a2dp data */ }; +void memcpy_changeendian(void *dst, const void *src, int size) +{ + int i; + const uint16_t *ptrsrc = src; + uint16_t *ptrdst = dst; + for (i = 0; i < size / 2; i++) { + *ptrdst++ = htons(*ptrsrc++); + } +} + static int bluetooth_start(snd_pcm_ioplug_t *io) { DBG("bluetooth_start %p", io); @@ -77,49 +135,21 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - DBG("bluetooth_pointer %p", io); - - DBG("hw_ptr=%lu", data->hw_ptr); +#if 0 + DBG("bluetooth_pointer %p, hw_ptr=%lu", io, data->hw_ptr); +#endif return data->hw_ptr; } static void bluetooth_exit(struct bluetooth_data *data) { - int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); - struct ipc_packet *pkt; - struct ipc_data_state *state; - - DBG("Sending PKT_TYPE_STATUS_REQ..."); - - if ((pkt = malloc(len)) == NULL) - goto done; - - memset(pkt, 0, len); - pkt->type = PKT_TYPE_STATE_REQ; - pkt->role = PKT_ROLE_NONE; - pkt->error = PKT_ERROR_NONE; - - state = (struct ipc_data_state *) pkt->data; - state->state = STATE_DISCONNECTED; - - if ((ret = send(data->sock, pkt, len, 0)) < 0) - DBG("Error %s (%d)", strerror(errno), errno); - - free(pkt); -done: - if (data == NULL) - return; - if (data->sock >= 0) close(data->sock); if (data->cfg.fd >= 0) close(data->cfg.fd); - if (data->buffer) - free(data->buffer); - free(data); } @@ -127,7 +157,7 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - DBG("bluetooth_close %p", io); + DBG("%p", io); bluetooth_exit(data); @@ -170,7 +200,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params return 0; opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDBUF : SO_RCVBUF; + SO_SNDBUF : SO_RCVBUF; if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) @@ -178,14 +208,15 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params err = errno; SNDERR("%s (%d)", strerror(err), err); + bluetooth_close(io); return -err; } -static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) +static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -193,8 +224,9 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, unsigned char *buff; int nrecv, frame_size = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu, io->nonblock=%u", - areas->step, areas->first, offset, size, io->nonblock); + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," + "io->nonblock=%u", areas->step, areas->first, offset, size, + io->nonblock); if (data->count > 0) goto proceed; @@ -216,10 +248,12 @@ static snd_pcm_sframes_t bluetooth_read(snd_pcm_ioplug_t *io, } /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % io->buffer_size; + data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % + io->buffer_size; proceed: - buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; + buff = (unsigned char *) areas->addr + + (areas->first + areas->step * offset) / 8; if ((data->count + size * frame_size) <= cfg.pkt_len) frames_to_write = size; @@ -238,10 +272,10 @@ done: return ret; } -static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) +static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -263,10 +297,11 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); /* Ready for more data */ - buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; + buff = (uint8_t *) areas->addr + + (areas->first + areas->step * offset) / 8; memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); - /* Remember we have some frame in the pipe now */ + /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; if (data->count != cfg.pkt_len) { ret = frames_to_read; @@ -290,28 +325,245 @@ static snd_pcm_sframes_t bluetooth_write(snd_pcm_ioplug_t *io, ret = -EIO; done: - DBG("returning %d", (int)ret); + DBG("returning %lu", ret); + return ret; +} + +static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + snd_pcm_uframes_t ret = 0; + return ret; +} + +static int avdtp_write(struct bluetooth_a2dp *a2dp, struct ipc_data_cfg *cfg, + unsigned int nonblock) +{ + int count = 0; + int written; + struct rtp_header *header; + struct rtp_payload *payload; +#ifdef ENABLE_DEBUG + static struct timeval send_date = { 0, 0 }; + static struct timeval prev_date = { 0, 0 }; + struct timeval send_delay = { 0, 0 }; + struct timeval sendz_delay = { 0, 0 }; +#endif + + header = (void *) a2dp->buffer; + payload = (void *) (a2dp->buffer + sizeof(*header)); + + memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); + + payload->frame_count = a2dp->frame_count; + header->v = 2; + header->pt = 1; + header->sequence_number = htons(a2dp->seq_num); + header->timestamp = htonl(a2dp->nsamples); + header->ssrc = htonl(1); + + while (count++ < 50) { +#ifdef ENABLE_DEBUG + gettimeofday(&send_date, NULL); +#endif + written = send(cfg->fd, a2dp->buffer, a2dp->count, + nonblock ? MSG_DONTWAIT : 0); + +#ifdef ENABLE_DEBUG + if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { + long delay, real, theo, delta; + + delay = (long) (send_delay.tv_sec * 1000 + + send_delay.tv_usec / 1000), + real = (long) (sendz_delay.tv_sec * 1000 + + sendz_delay.tv_usec / 1000); + theo = (long) (((float) a2dp->nsamples) / + cfg->rate * 1000.0); + delta = (long) (sendz_delay.tv_sec * 1000 + + sendz_delay.tv_usec / 1000) - + (long) (((float) a2dp->nsamples) / + cfg->rate * 1000.0); + + timersub(&send_date, &prev_date, &send_delay); + timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); + + DBG("send %d (cumul=%d) samples (delay=%ld ms," + " real=%ld ms, theo=%ld ms," + " delta=%ld ms).", a2dp->samples, + a2dp->nsamples, delay, real, theo, + delta); + } +#endif + if (written >= 0) + break; + + if (errno != EAGAIN) + break; + + DBG("send (retry)."); + usleep(150000); + } + +#ifdef ENABLE_DEBUG + prev_date = send_date; +#endif + + /* Send our data */ + if (written != a2dp->count) + DBG("Wrote %d not %d bytes", written, a2dp->count); +#if 0 + else { + /* Measure bandwith usage */ + struct timeval now = { 0, 0 }; + struct timeval interval = { 0, 0 }; + + if(a2dp->bandwithtimestamp.tv_sec == 0) + gettimeofday(&a2dp->bandwithtimestamp, NULL); + + /* See if we must wait again */ + gettimeofday(&now, NULL); + timersub(&now, &a2dp->bandwithtimestamp, &interval); + if(interval.tv_sec > 0) + DBG("Bandwith: %d (%d kbps)", a2dp->bandwithcount, + a2dp->bandwithcount/128); + a2dp->bandwithtimestamp = now; + a2dp->bandwithcount = 0; + } + + a2dp->bandwithcount += written; + +#endif + /* Reset buffer of data to send */ + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + a2dp->frame_count = 0; + a2dp->samples = 0; + a2dp->seq_num++; + + return written; +} + +static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp = &data->a2dp; + snd_pcm_sframes_t ret = 0; + snd_pcm_uframes_t frames_to_read; + int frame_size, encoded; + uint8_t *buff; + static int codesize = 0; + + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," + "io->nonblock=%u", areas->step, areas->first, + offset, size, io->nonblock); + + if (codesize == 0) { + /* How much data can be encoded by sbc at a time? */ + codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + /* Reserv header space in outgoing buffer */ + a2dp->count = sizeof(struct rtp_header) + + sizeof(struct rtp_payload); + gettimeofday(&a2dp->ntimestamp, NULL); + } + + frame_size = areas->step / 8; + if ((data->count + size * frame_size) <= codesize) + frames_to_read = size; + else + frames_to_read = (codesize - data->count) / frame_size; + + DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); + DBG("a2dp.count = %d cfg.pkt_len = %d", a2dp->count, + data->cfg.pkt_len); + + /* FIXME: If state is not streaming then return */ + + /* Ready for more data */ + buff = (uint8_t *) areas->addr + + (areas->first + areas->step * offset) / 8; + memcpy_changeendian(data->buffer + data->count, buff, + frame_size * frames_to_read); + + /* Remember we have some frames in the pipe now */ + data->count += frames_to_read * frame_size; + if (data->count != codesize) { + ret = frames_to_read; + goto done; + } + + /* Enough data to encode (sbc wants 1k blocks) */ + encoded = sbc_encode(&(a2dp->sbc), data->buffer, codesize); + if (encoded <= 0) { + DBG("Encoding error %d", encoded); + goto done; + } + + data->count -= encoded; + + DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); + + if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) + avdtp_write(a2dp, &data->cfg, io->nonblock); + + memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); + a2dp->count += a2dp->sbc.len; + a2dp->frame_count++; + a2dp->samples += encoded / frame_size; + a2dp->nsamples += encoded / frame_size; + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + codesize / frame_size) + % io->buffer_size; + + ret = frames_to_read; + +done: + DBG("returning %lu", ret); return ret; } -static snd_pcm_ioplug_callback_t bluetooth_playback_callback = { +static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_write, +}; + +static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, .hw_params = bluetooth_hw_params, .prepare = bluetooth_prepare, - .transfer = bluetooth_write, + .transfer = bluetooth_hsp_read, }; -static snd_pcm_ioplug_callback_t bluetooth_capture_callback = { +static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, .hw_params = bluetooth_hw_params, .prepare = bluetooth_prepare, - .transfer = bluetooth_read, + .transfer = bluetooth_a2dp_write, +}; + +static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_read, }; #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -390,7 +642,6 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) }; ret = recvmsg(data->sock, &msgh, 0); - if (ret < 0) { err = errno; SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); @@ -415,102 +666,143 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) return -EINVAL; } -static int bluetooth_cfg(struct bluetooth_data *data) +static int bluetooth_a2dp_init(struct bluetooth_data *data, + struct ipc_codec_sbc *sbc) { - int ret, len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); - struct ipc_packet *pkt; + struct bluetooth_a2dp *a2dp = &data->a2dp; + struct ipc_data_cfg *cfg = &data->cfg; - DBG("Sending PKT_TYPE_CFG_REQ..."); + if (cfg == NULL) { + SNDERR("Error getting codec parameters"); + return -1; + } - if ((pkt = malloc(len)) == 0) - return -ENOMEM; + if (cfg->codec != CFG_CODEC_SBC) + return -1; + + /* FIXME: init using flags? */ + sbc_init(&a2dp->sbc, 0); + a2dp->sbc.rate = cfg->rate; + a2dp->sbc.channels = cfg->channels; + if (cfg->channel_mode == CFG_CHANNEL_MODE_MONO || + cfg->channel_mode == CFG_CHANNEL_MODE_JOINT_STEREO) + a2dp->sbc.joint = 1; + a2dp->sbc.allocation = sbc->allocation; + a2dp->sbc.subbands = sbc->subbands; + a2dp->sbc.blocks = sbc->blocks; + a2dp->sbc.bitpool = sbc->bitpool; - memset(pkt, 0, len); + return 0; +} + +static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +{ + int ret, total; + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + + DBG("Sending PKT_TYPE_CFG_REQ..."); + + memset(buf, 0, sizeof(buf)); pkt->type = PKT_TYPE_CFG_REQ; pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; - if ((ret = send(data->sock, pkt, len, 0)) < 0) { - ret = -errno; - goto done; - } else if (ret == 0) { - ret = -EIO; - goto done; - } + ret = send(data->sock, pkt, sizeof(struct ipc_packet), 0); + if (ret < 0) + return -errno; + else if (ret == 0) + return -EIO; - DBG("OK - %d bytes sent", ret); + DBG("OK - %d bytes sent. Waiting for response...", ret); - DBG("Waiting for response..."); + memset(buf, 0, sizeof(buf)); - memset(pkt, 0, len); - if ((ret = recv(data->sock, pkt, len, 0)) < 0) { - ret = -errno; - goto done; - } else if (ret == 0) { - ret = -EIO; - goto done; - } + ret = recv(data->sock, buf, sizeof(*pkt) + sizeof(*cfg), 0); + if (ret < 0) + return -errno; + else if (ret == 0) + return -EIO; - DBG("OK - %d bytes received", ret); + total = ret; if (pkt->type != PKT_TYPE_CFG_RSP) { SNDERR("Unexpected packet type received: type = %d", pkt->type); - ret = -EINVAL; - goto done; + return -EINVAL; } if (pkt->error != PKT_ERROR_NONE) { SNDERR("Error while configuring device: error = %d", pkt->error); - ret = pkt->error; - goto done; + return pkt->error; } - if (pkt->length != sizeof(struct ipc_data_cfg)) { + if (cfg->codec != CFG_CODEC_SBC) + goto done; + + ret = recv(data->sock, sbc, sizeof(*sbc), 0); + if (ret < 0) + return -errno; + else if (ret == 0) + return -EIO; + + total += ret; + +done: + DBG("OK - %d bytes received", total); + + if (pkt->length != (total - sizeof(struct ipc_packet))) { SNDERR("Error while configuring device: packet size doesn't " "match"); - ret = -EINVAL; - goto done; + return -EINVAL; } - memcpy(&data->cfg, pkt->data, sizeof(struct ipc_data_cfg)); + memcpy(&data->cfg, cfg, sizeof(*cfg)); DBG("Device configuration:"); - DBG("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," - "rate=%u", data->cfg.fd, data->cfg.fd_opt, - data->cfg.channels, data->cfg.pkt_len, - data->cfg.sample_size, data->cfg.rate); + DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n" + "\tsample_size=%u\n\trate=%u", data->cfg.fd, + data->cfg.fd_opt, data->cfg.channels, data->cfg.pkt_len, + data->cfg.sample_size, data->cfg.rate); + + if (data->cfg.codec == CFG_CODEC_SBC) { + struct bluetooth_a2dp *a2dp = &data->a2dp; + ret = bluetooth_a2dp_init(data, sbc); + if (ret < 0) + return ret; + printf("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\t" + "bitpool=%u\n", a2dp->sbc.allocation, + a2dp->sbc.subbands, a2dp->sbc.blocks, + a2dp->sbc.bitpool); + } if (data->cfg.fd == -1) { SNDERR("Error while configuring device: could not acquire " "audio socket"); - ret = -EINVAL; - goto done; + return -EINVAL; } - if ((ret = bluetooth_recvmsg_fd(data)) < 0) - goto done; - - if ((data->buffer = malloc(data->cfg.pkt_len)) == 0) - return -ENOMEM; + ret = bluetooth_recvmsg_fd(data); + if (ret < 0) + return ret; /* It is possible there is some outstanding data in the pipe - we have to empty it */ - while(recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, - MSG_DONTWAIT) > 0); + while (recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, + MSG_DONTWAIT) > 0); memset(data->buffer, 0, data->cfg.pkt_len); -done: - free(pkt); - return ret; + return 0; } -static int bluetooth_init(struct bluetooth_data *data) +static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf) { - int sk, err, id; + int sk, err; struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; @@ -522,28 +814,24 @@ static int bluetooth_init(struct bluetooth_data *data) data->sock = -1; - id = abs(getpid() * rand()); - - if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - err = -errno; - SNDERR("Can't open socket"); - return -errno; + sk = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sk < 0) { + err = errno; + SNDERR("Cannot open socket: %s (%d)", strerror(err), err); + return -err; } DBG("Connecting to address: %s", addr.sun_path + 1); if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - SNDERR("Can't connect socket"); + err = errno; + SNDERR("Connection fail", strerror(err), err); close(sk); - return err; + return -err; } data->sock = sk; - if ((err = bluetooth_cfg(data)) < 0) - return err; - - return 0; + return bluetooth_cfg(data, conf); } SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) @@ -555,27 +843,32 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); data = malloc(sizeof(struct bluetooth_data)); - memset(data, 0, sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; } - err = bluetooth_init(data); + err = bluetooth_init(data, conf); if (err < 0) goto error; data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; data->io.mmap_rw = 0; /* No direct mmap communication */ - - data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? - &bluetooth_playback_callback : &bluetooth_capture_callback; data->io.poll_fd = data->cfg.fd; data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; data->io.private_data = data; + if (data->cfg.codec == CFG_CODEC_SBC) + data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? + &bluetooth_a2dp_playback : + &bluetooth_a2dp_capture; + else + data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? + &bluetooth_hsp_playback : + &bluetooth_hsp_capture; + err = snd_pcm_ioplug_create(&data->io, name, stream, mode); if (err < 0) goto error; diff --git a/audio/sink.c b/audio/sink.c index 436dfbce..f7e32647 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -25,4 +25,433 @@ #include #endif +#include + +#include +#include + +#include "dbus.h" +#include "dbus-helper.h" +#include "logging.h" + #include "avdtp.h" +#include "device.h" +#include "a2dp.h" +#include "error.h" +#include "unix.h" + +struct pending_connect { + DBusMessage *msg; + struct ipc_packet *pkt; + int pkt_len; + int sock; +}; + +struct sink { + struct avdtp *session; + struct avdtp_stream *stream; + uint8_t state; + struct pending_connect *c; + DBusConnection *conn; +}; + +static void pending_connect_free(struct pending_connect *c) +{ + if (c->pkt) + g_free(c->pkt); + if (c->msg) + dbus_message_unref(c->msg); + g_free(c); +} + +void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, void *user_data) +{ + struct device *dev = user_data; + struct sink *sink = dev->sink; + struct pending_connect *c = NULL; + DBusMessage *reply; + int cmd_err; + + if (err) + goto failed; + + switch (new_state) { + case AVDTP_STATE_IDLE: + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + "Disconnected", + DBUS_TYPE_INVALID); + if (sink->session) { + avdtp_unref(sink->session); + sink->session = NULL; + } + c = sink->c; + break; + case AVDTP_STATE_CONFIGURED: + cmd_err = avdtp_open(sink->session, stream); + if (cmd_err < 0) { + error("Error on avdtp_open %s (%d)", strerror(-cmd_err), + cmd_err); + goto failed; + } + break; + case AVDTP_STATE_OPEN: + if (old_state == AVDTP_STATE_CONFIGURED) + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + "Connected", + DBUS_TYPE_INVALID); + if (sink->c && sink->c->pkt) { + cmd_err = avdtp_start(sink->session, stream); + if (cmd_err < 0) { + error("Error on avdtp_start %s (%d)", + strerror(-cmd_err), cmd_err); + goto failed; + } + } + else + c = sink->c; + break; + case AVDTP_STATE_STREAMING: + c = sink->c; + break; + case AVDTP_STATE_CLOSING: + break; + case AVDTP_STATE_ABORTING: + break; + } + + sink->state = new_state; + + if (c) { + if (c->msg) { + reply = dbus_message_new_method_return(c->msg); + send_message_and_unref(dev->conn, reply); + } + if (c->pkt) { + struct ipc_data_cfg *rsp; + int ret; + + ret = sink_get_config(dev, c->sock, c->pkt, + c->pkt_len, &rsp); + if (ret == 0) { + unix_send_cfg(c->sock, rsp); + g_free(rsp); + } + else + unix_send_cfg(c->sock, NULL); + } + + pending_connect_free(c); + sink->c = NULL; + } + + return; + +failed: + if (sink->c) { + if (sink->c->msg && err) + err_failed(dev->conn, sink->c->msg, + avdtp_strerror(err)); + + pending_connect_free(sink->c); + sink->c = NULL; + } + + if (new_state == AVDTP_STATE_IDLE) { + avdtp_unref(sink->session); + sink->session = NULL; + } +} + + + +static void discovery_complete(struct avdtp *session, GSList *seps, int err, + void *user_data) +{ + struct device *dev = user_data; + struct sink *sink = dev->sink; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + GSList *caps = NULL; + const char *err_str = NULL; + + if (err < 0) { + error("Discovery failed"); + err_str = strerror(-err); + goto failed; + } + + debug("Discovery complete"); + + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + A2DP_CODEC_SBC, &lsep, &rsep) < 0) { + err_str = "No matching ACP and INT SEPs found"; + goto failed; + } + + if (!a2dp_select_capabilities(rsep, &caps)) { + err_str = "Unable to select remote SEP capabilities"; + goto failed; + } + + err = avdtp_set_configuration(session, rsep, lsep, caps, + &sink->stream); + if (err < 0) { + error("avdtp_set_configuration: %s", strerror(-err)); + err_str = "Unable to set configuration"; + goto failed; + } + + avdtp_stream_set_cb(session, sink->stream, stream_state_changed, dev); + + return; + +failed: + error("%s", err_str); + if (sink->c) { + if (sink->c->msg) + err_failed(dev->conn, sink->c->msg, err_str); + pending_connect_free(sink->c); + sink->c = NULL; + } + if (sink->session) { + avdtp_unref(sink->session); + sink->session = NULL; + } +} + +static DBusHandlerResult sink_connect(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct device *dev = data; + struct sink *sink = dev->sink; + struct pending_connect *c; + int err; + + if (!sink->session) + sink->session = avdtp_get(&dev->src, &dev->dst); + + if (sink->c) + return err_connect_failed(conn, msg, "Connect in progress"); + + if (sink->state >= AVDTP_STATE_OPEN) + return err_already_connected(conn, msg); + + c = g_new0(struct pending_connect, 1); + c->msg = dbus_message_ref(msg); + sink->c = c; + + err = avdtp_discover(sink->session, discovery_complete, data); + if (err < 0) { + dbus_message_unref(c->msg); + pending_connect_free(c); + sink->c = NULL; + return err_connect_failed(conn, msg, strerror(err)); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult sink_disconnect(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct device *device = data; + struct sink *sink = device->sink; + struct pending_connect *c; + int err; + + if (!sink->session) + return err_not_connected(conn, msg); + + if (sink->c) + return err_failed(conn, msg, strerror(EBUSY)); + + if (sink->state < AVDTP_STATE_OPEN) { + avdtp_unref(sink->session); + sink->session = NULL; + } else { + err = avdtp_close(sink->session, sink->stream); + if (err < 0) + return err_failed(conn, msg, strerror(-err)); + } + + c = g_new0(struct pending_connect, 1); + c->msg = dbus_message_ref(msg); + sink->c = c; + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult sink_is_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + struct sink *sink = device->sink; + DBusMessage *reply; + dbus_bool_t connected; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + connected = (sink->state != AVDTP_STATE_IDLE); + + 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 sink_methods[] = { + { "Connect", sink_connect, "", "" }, + { "Disconnect", sink_disconnect, "", "" }, + { "IsConnected", sink_is_connected, "", "b" }, + { NULL, NULL, NULL, NULL } +}; + +static DBusSignalVTable sink_signals[] = { + { "Connected", "" }, + { "Disconnected", "" }, + { NULL, NULL } +}; + +struct sink *sink_init(void *device) +{ + struct device *dev = device; + + if (!dbus_connection_register_interface(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + sink_methods, + sink_signals, NULL)) + return NULL; + + return g_new0(struct sink, 1); +} + +void sink_free(void *device) +{ + struct device *dev = device; + struct sink *sink = dev->sink; + + if (sink->session) + avdtp_unref(sink->session); + + if (sink->c) + pending_connect_free(sink->c); + + g_free(sink); + dev->sink = NULL; +} + +int sink_get_config(void *device, int sock, struct ipc_packet *req, + int pkt_len, struct ipc_data_cfg **rsp) +{ + struct device *dev = device; + struct sink *sink = dev->sink; + int err = EINVAL; + struct pending_connect *c = NULL; + + if (sink->state == AVDTP_STATE_STREAMING) + goto proceed; + + if (!sink->session) + sink->session = avdtp_get(&dev->src, &dev->dst); + + c = g_new0(struct pending_connect, 1); + c->sock = sock; + c->pkt = g_malloc(pkt_len); + memcpy(c->pkt, req, pkt_len); + sink->c = c; + + if (sink->state == AVDTP_STATE_IDLE) + err = avdtp_discover(sink->session, discovery_complete, device); + else if (sink->state < AVDTP_STATE_STREAMING) + err = avdtp_start(sink->session, sink->stream); + else + goto error; + + if (err < 0) + goto error; + + return 1; + +proceed: + if (!a2dp_get_config(sink->stream, rsp)) + goto error; + + return 0; + +error: + if (c) + pending_connect_free(c); + return -err; +} + +gboolean sink_is_active(void *device) +{ + struct device *dev = device; + struct sink *sink = dev->sink; + + if (sink->session) + return TRUE; + + return FALSE; +} + +void sink_set_state(void *device, avdtp_state_t state) +{ + struct device *dev = device; + struct sink *sink = dev->sink; + int err = 0; + + if (sink->state == state) + return; + + if (!sink->session || !sink->stream) + goto failed; + + switch (sink->state) { + case AVDTP_STATE_OPEN: + if (state == AVDTP_STATE_STREAMING) { + err = avdtp_start(sink->session, sink->stream); + if (err == 0) + return; + } + else if (state == AVDTP_STATE_IDLE) { + err = avdtp_close(sink->session, sink->stream); + if (err == 0) + return; + } + break; + case AVDTP_STATE_STREAMING: + if (state == AVDTP_STATE_OPEN) { + err = avdtp_suspend(sink->session, sink->stream); + if (err == 0) + return; + } + else if (state == AVDTP_STATE_IDLE) { + err = avdtp_close(sink->session, sink->stream); + if (err == 0) + return; + } + break; + default: + goto failed; + } +failed: + error("%s: Error changing states", dev->path); +} + +avdtp_state_t sink_get_state(void *device) +{ + struct device *dev = device; + struct sink *sink = dev->sink; + + return sink->state; +} diff --git a/audio/sink.h b/audio/sink.h index 4cc3a0e4..9d65e278 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -21,7 +21,18 @@ * */ +#include "ipc.h" +#include "avdtp.h" + #define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" struct sink; +struct sink *sink_init(void *device); +void sink_new_stream(void *device, void *lsep); +void sink_free(void *device); +int sink_get_config(void *device, int sock, struct ipc_packet *req, + int pkt_len, struct ipc_data_cfg **rsp); +gboolean sink_is_active(void *device); +void sink_set_state(void *device, avdtp_state_t state); +avdtp_state_t sink_get_state(void *device); diff --git a/audio/unix.c b/audio/unix.c index fbda7ed9..0880f4ff 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -37,11 +37,28 @@ #include "logging.h" #include "dbus.h" - #include "manager.h" +#include "ipc.h" +#include "unix.h" + +struct unix_client { + struct device *dev; + int sock; +}; + +static GSList *clients = NULL; static int unix_sock = -1; +static int unix_send_state(int sock, struct ipc_packet *pkt); + +static void client_free(struct unix_client *client) +{ + if (client->sock >= 0) + close(client->sock); + g_free(client); +} + /* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the handle of the @@ -75,52 +92,120 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) return sendmsg(sock, &msgh, MSG_NOSIGNAL); } -static void cfg_event(int clisk, struct ipc_packet *pkt) +static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, + int len) { - struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; - struct device *device; + struct ipc_data_cfg *rsp; + struct device *dev; + int ret; - memset(cfg, 0, sizeof(struct ipc_data_cfg)); + dev = manager_get_connected_device(); + if (dev) + goto proceed; - if ((device = manager_default_device())) { - if (device->headset) - headset_get_config(device, clisk, pkt); - } - else - cfg->fd = -1; + dev = manager_default_device(); + if (!dev) + goto failed; - if (cfg->fd != 0) - unix_send_cfg(clisk, pkt); +proceed: + ret = device_get_config(dev, client->sock, pkt, len, &rsp); + if (ret < 0) + goto failed; + + client->dev = dev; + + /* Connecting in progress */ + if (ret == 1) + return; + + unix_send_cfg(client->sock, rsp); + g_free(rsp); + + return; + +failed: + unix_send_cfg(client->sock, NULL); } -static void ctl_event(int clisk, struct ipc_packet *pkt) +static void ctl_event(struct unix_client *client, struct ipc_packet *pkt, + int len) { } -static void state_event(int clisk, struct ipc_packet *pkt) +static void state_event(struct unix_client *client, struct ipc_packet *pkt, + int len) { struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - struct device *device; + struct device *dev = client->dev; - if (!(device = manager_default_device())) - return; + if (len > sizeof(struct ipc_packet)) + device_set_state(dev, state->state); + else + state->state = device_get_state(dev); - if (device->headset) - headset_set_state(device, state->state); + unix_send_state(client->sock, pkt); +} + +static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + struct unix_client *client = data; + int len, len_check; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + debug("Unix client disconnected"); + device_set_state(client->dev, STATE_CONNECTED); + goto failed; + } + + memset(buf, 0, sizeof(buf)); + + len = recv(client->sock, buf, sizeof(buf), 0); + if (len < 0) { + error("recv: %s (%d)", strerror(errno), errno); + goto failed; + } + + len_check = pkt->length + sizeof(struct ipc_packet); + if (len != len_check) { + error("Packet lenght doesn't match"); + goto failed; + } + + switch (pkt->type) { + case PKT_TYPE_CFG_REQ: + info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); + cfg_event(client, pkt, len); + break; + case PKT_TYPE_STATE_REQ: + info("Package PKT_TYPE_STATE_REQ"); + state_event(client, pkt, len); + break; + case PKT_TYPE_CTL_REQ: + info("Package PKT_TYPE_CTL_REQ"); + ctl_event(client, pkt, len); + break; + } - unix_send_status(clisk, pkt); + return TRUE; - g_free(pkt); +failed: + clients = g_slist_remove(clients, client); + client_free(client); + return FALSE; } -static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) +static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { struct sockaddr_un addr; socklen_t addrlen; - struct ipc_packet *pkt; - int sk, clisk, len; - - debug("chan %p cond %td data %p", chan, cond, data); + int sk, cli_sk; + struct unix_client *client; + GIOChannel *io; if (cond & G_IO_NVAL) return FALSE; @@ -135,32 +220,22 @@ static gboolean unix_event(GIOChannel *chan, GIOCondition cond, gpointer data) memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); - clisk = accept(sk, (struct sockaddr *) &addr, &addrlen); - if (clisk < 0) { + cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen); + if (cli_sk < 0) { error("accept: %s (%d)", strerror(errno), errno); return TRUE; } - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); - pkt = g_malloc0(len); - len = recv(clisk, pkt, len, 0); + debug("Accepted new client connection on unix socket"); - debug("path %s len %d", addr.sun_path + 1, len); + client = g_new(struct unix_client, 1); + client->sock = cli_sk; + clients = g_slist_append(clients, client); - switch (pkt->type) { - case PKT_TYPE_CFG_REQ: - info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); - cfg_event(clisk, pkt); - break; - case PKT_TYPE_STATE_REQ: - info("Package PKT_TYPE_STATE_REQ"); - state_event(clisk, pkt); - break; - case PKT_TYPE_CTL_REQ: - info("Package PKT_TYPE_CTL_REQ"); - ctl_event(clisk, pkt); - break; - } + io = g_io_channel_unix_new(cli_sk); + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + client_cb, client); + g_io_channel_unref(io); return TRUE; } @@ -194,10 +269,8 @@ int unix_init(void) listen(sk, 1); io = g_io_channel_unix_new(sk); - g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - unix_event, NULL); - + server_cb, NULL); g_io_channel_unref(io); info("Unix socket created: %d", sk); @@ -207,59 +280,77 @@ int unix_init(void) void unix_exit(void) { + g_slist_foreach(clients, (GFunc) client_free, NULL); + g_slist_free(clients); close(unix_sock); unix_sock = -1; } -int unix_send_cfg(int sock, struct ipc_packet *pkt) +int unix_send_cfg(int sock, struct ipc_data_cfg *cfg) { - struct ipc_data_cfg *cfg = (struct ipc_data_cfg *) pkt->data; - int len; + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + int len, codec_len; - info("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u, sample_size=%u," - "rate=%u", cfg->fd, cfg->fd_opt, cfg->channels, - cfg->pkt_len, cfg->sample_size, cfg->rate); + memset(buf, 0, sizeof(buf)); pkt->type = PKT_TYPE_CFG_RSP; - pkt->length = sizeof(struct ipc_data_cfg); + + if (!cfg) { + pkt->error = EINVAL; + len = send(sock, pkt, sizeof(struct ipc_packet), 0); + if (len < 0) + error("send: %s (%d)", strerror(errno), errno); + return len; + } + + debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u," + "sample_size=%u, rate=%u", cfg->fd, cfg->fd_opt, + cfg->channels, cfg->pkt_len, cfg->sample_size, cfg->rate); + + if (cfg->codec == CFG_CODEC_SBC) + codec_len = sizeof(struct ipc_codec_sbc); + else + codec_len = 0; + pkt->error = PKT_ERROR_NONE; + pkt->length = sizeof(struct ipc_data_cfg) + codec_len; + memcpy(pkt->data, cfg, pkt->length); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_cfg); + len = sizeof(struct ipc_packet) + pkt->length; len = send(sock, pkt, len, 0); if (len < 0) - info("Error %s(%d)", strerror(errno), errno); + error("Error %s(%d)", strerror(errno), errno); - info("%d bytes sent", len); + debug("%d bytes sent", len); if (cfg->fd != -1) { len = unix_sendmsg_fd(sock, cfg->fd, pkt); if (len < 0) - info("Error %s(%d)", strerror(errno), errno); - info("%d bytes sent", len); + error("Error %s(%d)", strerror(errno), errno); + debug("%d bytes sent", len); } - g_free(pkt); return 0; } -int unix_send_status(int sock, struct ipc_packet *pkt) +static int unix_send_state(int sock, struct ipc_packet *pkt) { struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; int len; info("status=%u", state->state); - pkt->type = PKT_TYPE_CFG_RSP; + pkt->type = PKT_TYPE_STATE_RSP; pkt->length = sizeof(struct ipc_data_state); pkt->error = PKT_ERROR_NONE; len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); len = send(sock, pkt, len, 0); if (len < 0) - info("Error %s(%d)", strerror(errno), errno); + error("Error %s(%d)", strerror(errno), errno); - info("%d bytes sent", len); + debug("%d bytes sent", len); - g_free(pkt); return 0; } diff --git a/audio/unix.h b/audio/unix.h index 32cf4af9..c771b965 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -24,7 +24,6 @@ #include "ipc.h" int unix_init(void); - void unix_exit(void); -int unix_send_cfg(int sock, struct ipc_packet *pkt); -int unix_send_status(int sock, struct ipc_packet *pkt); + +int unix_send_cfg(int sock, struct ipc_data_cfg *cfg); -- cgit From d317144c04b6d52c354a7cdd11a62adf6de42b64 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 12:18:22 +0000 Subject: Fix avdtp session reference counting --- audio/avdtp.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 8615a18a..cf6060e4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -349,8 +349,6 @@ static gboolean disconnect_timeout(gpointer user_data) assert(session->ref == 1); - sessions = g_slist_remove(sessions, session); - session->dc_timer = 0; avdtp_unref(session); @@ -1612,7 +1610,7 @@ static struct avdtp *find_session(bdaddr_t *src, bdaddr_t *dst) return NULL; } -struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) +static struct avdtp *avdtp_get_internal(bdaddr_t *src, bdaddr_t *dst) { struct avdtp *session; @@ -1633,6 +1631,15 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) sessions = g_slist_append(sessions, session); + return session; +} + +struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) +{ + struct avdtp *session; + + session = avdtp_get_internal(src, dst); + return avdtp_ref(session); } @@ -2053,7 +2060,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - session = avdtp_get(&src, &dst); + session = avdtp_get_internal(&src, &dst); if (session->pending_open && session->pending_open->open_acp) { handle_transport_connect(session, cli_sk, l2o.imtu); -- cgit From 36a07968fcc69442e3248caa46e74de18bacea75 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 11 Aug 2007 12:56:42 +0000 Subject: Create objects for default device and sink --- audio/test-audio | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'audio') diff --git a/audio/test-audio b/audio/test-audio index 5553f0a5..19ea6252 100755 --- a/audio/test-audio +++ b/audio/test-audio @@ -17,3 +17,11 @@ try: 'org.bluez.audio.Headset') except: pass + +try: + device = dbus.Interface(bus.get_object(conn, audio.DefaultDevice()), + 'org.bluez.audio.Device') + sink = dbus.Interface(bus.get_object(conn, audio.DefaultDevice()), + 'org.bluez.audio.Sink') +except: + pass -- cgit From 37b86c3b3bf4641c7f41c701f531ffbfc5ebe720 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 13:02:10 +0000 Subject: Implement support for receiving the Close command --- audio/avdtp.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index cf6060e4..2d71e272 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -171,6 +171,10 @@ struct abort_resp { struct avdtp_header header; } __attribute__ ((packed)); +struct close_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + struct stream_pause_resp { struct avdtp_header header; uint8_t rfa0:2; @@ -309,6 +313,9 @@ static gboolean avdtp_send(struct avdtp *session, void *data, int len) { int ret; + if (session->sock < 0) + return FALSE; + ret = send(session->sock, data, len, 0); if (ret < 0) @@ -332,7 +339,6 @@ static void pending_req_free(struct pending_req *req) g_free(req); } -#if 0 static gboolean stream_close_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; @@ -341,7 +347,6 @@ static gboolean stream_close_timeout(gpointer user_data) return FALSE; } -#endif static gboolean disconnect_timeout(gpointer user_data) { @@ -389,6 +394,8 @@ static void stream_free(struct avdtp_stream *stream) stream->lsep->info.inuse = 0; stream->lsep->stream = NULL; stream->rsep->stream = NULL; + if (stream->close_timer) + g_source_remove(stream->close_timer); if (stream->caps) { g_slist_foreach(stream->caps, (GFunc) g_free, NULL); g_slist_free(stream->caps); @@ -717,7 +724,54 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct seid_req *req, static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct close_resp *rsp = (struct close_resp *) session->buf; + struct seid_rej rej; + uint8_t err; + gboolean ret; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_OPEN && + sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->close) { + if (!sep->ind->close(sep, stream, &err)) + goto failed; + } + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + init_response(&rsp->header, &req->header, TRUE); + + ret = avdtp_send(session, rsp, sizeof(struct close_resp)); + if (ret == TRUE) { + stream->close_timer = g_timeout_add(REQ_TIMEOUT, + stream_close_timeout, + stream); + } + + return ret; + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_suspend_cmd(struct avdtp *session, struct seid_req *req, -- cgit From 1b9bcb5ed4fe1301b32db6db1be9200c7c66348a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 14:19:51 +0000 Subject: Ensure that transport channels get disconnected before the signalling channel --- audio/avdtp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 2d71e272..bbb4314c 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -462,6 +462,9 @@ static void connection_lost(struct avdtp *session) if (session->discov_cb) finalize_discovery(session, -ECONNABORTED); + g_slist_foreach(session->streams, (GFunc) release_stream, session); + session->streams = NULL; + if (session->sock >= 0) { close(session->sock); session->sock = -1; @@ -473,9 +476,6 @@ static void connection_lost(struct avdtp *session) g_source_remove(session->io); session->io = 0; } - - g_slist_foreach(session->streams, (GFunc) release_stream, session); - session->streams = NULL; } void avdtp_unref(struct avdtp *session) -- cgit From c1761757dfeb3bf1802c5486d652d8f3852f939e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 15:11:13 +0000 Subject: Get rid of some valgrind warnings caused by hci_get_route(NULL) calls --- audio/device.c | 2 +- audio/manager.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index af7cca80..1d39cb16 100644 --- a/audio/device.c +++ b/audio/device.c @@ -140,7 +140,7 @@ struct device *device_register(DBusConnection *conn, return NULL; bacpy(&src, BDADDR_ANY); - dev_id = hci_get_route(NULL); + dev_id = hci_get_route(&src); if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return NULL; diff --git a/audio/manager.c b/audio/manager.c index 58f19ed0..87b3b4f3 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1174,7 +1174,7 @@ static void register_devices_stored(const char *adapter) textfile_foreach(filename, parse_stored_devices, &src); bacpy(&default_src, BDADDR_ANY); - dev_id = hci_get_route(NULL); + dev_id = hci_get_route(&default_src); if (dev_id < 0) hci_devba(dev_id, &default_src); -- cgit From 9494c146cec1df466c2f331957748beeac8d745a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 15:52:27 +0000 Subject: Remove redundant stream fd from config response --- audio/a2dp.c | 7 ++++--- audio/a2dp.h | 3 ++- audio/device.c | 10 +++++----- audio/device.h | 2 +- audio/headset.c | 32 +++++++++++++++----------------- audio/headset.h | 2 +- audio/ipc.h | 1 - audio/pcm_bluetooth.c | 32 ++++++++++++++++---------------- audio/sink.c | 12 ++++++------ audio/sink.h | 2 +- audio/unix.c | 18 +++++++++--------- audio/unix.h | 2 +- 12 files changed, 61 insertions(+), 62 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 3afc133d..abd348c3 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -552,7 +552,8 @@ gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) return TRUE; } -gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg) +gboolean a2dp_get_config(struct avdtp_stream *stream, + struct ipc_data_cfg **cfg, int *fd) { struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap = NULL; @@ -563,10 +564,10 @@ gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg) rsp = g_malloc0(sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)); - rsp->fd = -1; + *fd = -1; sbc = (void *) rsp->data; - if (!avdtp_stream_get_transport(stream, &rsp->fd, &rsp->pkt_len, + if (!avdtp_stream_get_transport(stream, fd, &rsp->pkt_len, &caps)) { g_free(rsp); return FALSE; diff --git a/audio/a2dp.h b/audio/a2dp.h index 7e53f7a9..708f9f94 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -68,4 +68,5 @@ void a2dp_exit(void); gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); -gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg); +gboolean a2dp_get_config(struct avdtp_stream *stream, + struct ipc_data_cfg **cfg, int *fd); diff --git a/audio/device.c b/audio/device.c index 1d39cb16..18e494c1 100644 --- a/audio/device.c +++ b/audio/device.c @@ -252,16 +252,16 @@ void device_finish_sdp_transaction(struct device *dev) } int device_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp) + int pkt_len, struct ipc_data_cfg **rsp, int *fd) { if (dev->sink && sink_is_active(dev)) - return sink_get_config(dev, sock, req, pkt_len, rsp); + return sink_get_config(dev, sock, req, pkt_len, rsp, fd); else if (dev->headset && headset_is_active(dev)) - return headset_get_config(dev, sock, req, pkt_len, rsp); + return headset_get_config(dev, sock, req, pkt_len, rsp, fd); else if (dev->sink) - return sink_get_config(dev, sock, req, pkt_len, rsp); + return sink_get_config(dev, sock, req, pkt_len, rsp, fd); else if (dev->headset) - return headset_get_config(dev, sock, req, pkt_len, rsp); + return headset_get_config(dev, sock, req, pkt_len, rsp, fd); return -EINVAL; } diff --git a/audio/device.h b/audio/device.h index 62c13e9e..d7608741 100644 --- a/audio/device.h +++ b/audio/device.h @@ -77,7 +77,7 @@ int device_store(struct device *device, gboolean is_default); void device_finish_sdp_transaction(struct device *device); int device_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp); + int pkt_len, struct ipc_data_cfg **rsp, int *fd); void device_set_state(struct device *dev, uint8_t state); diff --git a/audio/headset.c b/audio/headset.c index 2bc8583b..9c91c925 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -343,16 +343,16 @@ static void pending_connect_ok(struct pending_connect *c, struct device *dev) } else if (c->pkt) { struct ipc_data_cfg *rsp; - int ret; + int ret, fd; - ret = headset_get_config(dev, c->sock, c->pkt, - c->pkt_len, &rsp); + ret = headset_get_config(dev, c->sock, c->pkt, c->pkt_len, + &rsp, &fd); if (ret == 0) { - unix_send_cfg(c->sock, rsp); + unix_send_cfg(c->sock, rsp, fd); g_free(rsp); } else - unix_send_cfg(c->sock, NULL); + unix_send_cfg(c->sock, NULL, -1); } pending_connect_free(c); @@ -363,7 +363,7 @@ static void pending_connect_failed(struct pending_connect *c, struct device *dev if (c->msg) err_connect_failed(dev->conn, c->msg, strerror(c->err)); if (c->pkt) - unix_send_cfg(c->sock, NULL); + unix_send_cfg(c->sock, NULL, -1); pending_connect_free(c); } @@ -1391,12 +1391,11 @@ void headset_free(void *device) } int headset_get_config(void *device, int sock, struct ipc_packet *pkt, - int pkt_len, struct ipc_data_cfg **cfg) + int pkt_len, struct ipc_data_cfg **cfg, int *fd) { struct headset *hs = ((struct device *) device)->headset; int err = EINVAL; struct pending_connect *c; - struct ipc_data_cfg *rsp; if (hs->rfcomm && hs->sco) goto proceed; @@ -1420,15 +1419,14 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt, proceed: *cfg = g_new0(struct ipc_data_cfg, 1); - rsp = *cfg; - rsp->fd = g_io_channel_unix_get_fd(hs->sco); - rsp->fd_opt = CFG_FD_OPT_READWRITE; - rsp->codec = CFG_CODEC_NONE; - rsp->channels = 1; - rsp->channel_mode = CFG_CHANNEL_MODE_MONO; - rsp->pkt_len = 48; - rsp->sample_size = 2; - rsp->rate = 8000; + (*cfg)->fd_opt = CFG_FD_OPT_READWRITE; + (*cfg)->codec = CFG_CODEC_NONE; + (*cfg)->channels = 1; + (*cfg)->channel_mode = CFG_CHANNEL_MODE_MONO; + (*cfg)->pkt_len = 48; + (*cfg)->sample_size = 2; + (*cfg)->rate = 8000; + *fd = g_io_channel_unix_get_fd(hs->sco); return 0; diff --git a/audio/headset.h b/audio/headset.h index 530bdea8..db1fef65 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -65,7 +65,7 @@ void headset_free(void *device); void headset_update(void *device, sdp_record_t *record, uint16_t svc); int headset_get_config(void *device, int sock, struct ipc_packet *pkt, - int pkt_len, struct ipc_data_cfg **rsp); + int pkt_len, struct ipc_data_cfg **rsp, int *fd); headset_type_t headset_get_type(void *device); void headset_set_type(void *device, headset_type_t type); diff --git a/audio/ipc.h b/audio/ipc.h index bd31abbc..473efc05 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -78,7 +78,6 @@ struct ipc_packet { #define CFG_CODEC_SBC 1 struct ipc_data_cfg { - int fd; /* Stream file descriptor */ uint8_t fd_opt; /* Stream file descriptor options: read, write or readwrite*/ uint8_t channels; /* Number of audio channel */ uint8_t channel_mode; /* Audio channel mode*/ diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 3f428ecd..b5f74c94 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -101,6 +101,7 @@ struct bluetooth_data { snd_pcm_ioplug_t io; snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ + int stream_fd; /* Audio stream filedescriptor */ int sock; /* Daemon unix socket */ uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ @@ -147,8 +148,8 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->sock >= 0) close(data->sock); - if (data->cfg.fd >= 0) - close(data->cfg.fd); + if (data->stream_fd >= 0) + close(data->stream_fd); free(data); } @@ -186,7 +187,6 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; uint32_t period_count = io->buffer_size / io->period_size; int opt_name, err; @@ -195,14 +195,14 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; - if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) return 0; opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDBUF : SO_RCVBUF; - if (setsockopt(cfg.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) return 0; @@ -233,7 +233,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, frame_size = areas->step / 8; - nrecv = recv(cfg.fd, data->buffer, cfg.pkt_len, + nrecv = recv(data->stream_fd, data->buffer, cfg.pkt_len, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); if (nrecv < 0) { @@ -308,7 +308,7 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, goto done; } - rsend = send(cfg.fd, data->buffer, cfg.pkt_len, + rsend = send(data->stream_fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ @@ -338,13 +338,13 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, return ret; } -static int avdtp_write(struct bluetooth_a2dp *a2dp, struct ipc_data_cfg *cfg, - unsigned int nonblock) +static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) { int count = 0; int written; struct rtp_header *header; struct rtp_payload *payload; + struct bluetooth_a2dp *a2dp = &data->a2dp; #ifdef ENABLE_DEBUG static struct timeval send_date = { 0, 0 }; static struct timeval prev_date = { 0, 0 }; @@ -368,7 +368,7 @@ static int avdtp_write(struct bluetooth_a2dp *a2dp, struct ipc_data_cfg *cfg, #ifdef ENABLE_DEBUG gettimeofday(&send_date, NULL); #endif - written = send(cfg->fd, a2dp->buffer, a2dp->count, + written = send(data->stream_fd, a2dp->buffer, a2dp->count, nonblock ? MSG_DONTWAIT : 0); #ifdef ENABLE_DEBUG @@ -508,7 +508,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) - avdtp_write(a2dp, &data->cfg, io->nonblock); + avdtp_write(data, io->nonblock); memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); a2dp->count += a2dp->sbc.len; @@ -655,8 +655,8 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - data->cfg.fd = (*(int *) CMSG_DATA(cmsg)); - DBG("fd = %d", data->cfg.fd); + data->stream_fd = (*(int *) CMSG_DATA(cmsg)); + DBG("stream_fd = %d", data->stream_fd); return 0; } } @@ -780,7 +780,7 @@ done: a2dp->sbc.bitpool); } - if (data->cfg.fd == -1) { + if (data->stream_fd == -1) { SNDERR("Error while configuring device: could not acquire " "audio socket"); return -EINVAL; @@ -792,7 +792,7 @@ done: /* It is possible there is some outstanding data in the pipe - we have to empty it */ - while (recv(data->cfg.fd, data->buffer, data->cfg.pkt_len, + while (recv(data->stream_fd, data->buffer, data->cfg.pkt_len, MSG_DONTWAIT) > 0); memset(data->buffer, 0, data->cfg.pkt_len); @@ -855,7 +855,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; data->io.mmap_rw = 0; /* No direct mmap communication */ - data->io.poll_fd = data->cfg.fd; + data->io.poll_fd = data->stream_fd; data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; data->io.private_data = data; diff --git a/audio/sink.c b/audio/sink.c index f7e32647..a660556d 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -132,16 +132,16 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, } if (c->pkt) { struct ipc_data_cfg *rsp; - int ret; + int ret, fd; ret = sink_get_config(dev, c->sock, c->pkt, - c->pkt_len, &rsp); + c->pkt_len, &rsp, &fd); if (ret == 0) { - unix_send_cfg(c->sock, rsp); + unix_send_cfg(c->sock, rsp, fd); g_free(rsp); } else - unix_send_cfg(c->sock, NULL); + unix_send_cfg(c->sock, NULL, -1); } pending_connect_free(c); @@ -350,7 +350,7 @@ void sink_free(void *device) } int sink_get_config(void *device, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp) + int pkt_len, struct ipc_data_cfg **rsp, int *fd) { struct device *dev = device; struct sink *sink = dev->sink; @@ -382,7 +382,7 @@ int sink_get_config(void *device, int sock, struct ipc_packet *req, return 1; proceed: - if (!a2dp_get_config(sink->stream, rsp)) + if (!a2dp_get_config(sink->stream, rsp, fd)) goto error; return 0; diff --git a/audio/sink.h b/audio/sink.h index 9d65e278..b14a29a7 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -32,7 +32,7 @@ struct sink *sink_init(void *device); void sink_new_stream(void *device, void *lsep); void sink_free(void *device); int sink_get_config(void *device, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp); + int pkt_len, struct ipc_data_cfg **rsp, int *fd); gboolean sink_is_active(void *device); void sink_set_state(void *device, avdtp_state_t state); avdtp_state_t sink_get_state(void *device); diff --git a/audio/unix.c b/audio/unix.c index 0880f4ff..355a7403 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -97,7 +97,7 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, { struct ipc_data_cfg *rsp; struct device *dev; - int ret; + int ret, fd; dev = manager_get_connected_device(); if (dev) @@ -108,7 +108,7 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, goto failed; proceed: - ret = device_get_config(dev, client->sock, pkt, len, &rsp); + ret = device_get_config(dev, client->sock, pkt, len, &rsp, &fd); if (ret < 0) goto failed; @@ -118,13 +118,13 @@ proceed: if (ret == 1) return; - unix_send_cfg(client->sock, rsp); + unix_send_cfg(client->sock, rsp, fd); g_free(rsp); return; failed: - unix_send_cfg(client->sock, NULL); + unix_send_cfg(client->sock, NULL, -1); } static void ctl_event(struct unix_client *client, struct ipc_packet *pkt, @@ -286,7 +286,7 @@ void unix_exit(void) unix_sock = -1; } -int unix_send_cfg(int sock, struct ipc_data_cfg *cfg) +int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) { char buf[IPC_MTU]; struct ipc_packet *pkt = (void *) buf; @@ -305,8 +305,8 @@ int unix_send_cfg(int sock, struct ipc_data_cfg *cfg) } debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u," - "sample_size=%u, rate=%u", cfg->fd, cfg->fd_opt, - cfg->channels, cfg->pkt_len, cfg->sample_size, cfg->rate); + "sample_size=%u, rate=%u", fd, cfg->fd_opt, cfg->channels, + cfg->pkt_len, cfg->sample_size, cfg->rate); if (cfg->codec == CFG_CODEC_SBC) codec_len = sizeof(struct ipc_codec_sbc); @@ -324,8 +324,8 @@ int unix_send_cfg(int sock, struct ipc_data_cfg *cfg) debug("%d bytes sent", len); - if (cfg->fd != -1) { - len = unix_sendmsg_fd(sock, cfg->fd, pkt); + if (fd != -1) { + len = unix_sendmsg_fd(sock, fd, pkt); if (len < 0) error("Error %s(%d)", strerror(errno), errno); debug("%d bytes sent", len); diff --git a/audio/unix.h b/audio/unix.h index c771b965..83e2518a 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -26,4 +26,4 @@ int unix_init(void); void unix_exit(void); -int unix_send_cfg(int sock, struct ipc_data_cfg *cfg); +int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd); -- cgit From 2edfb968d931f6ec83964aadb5883dd330b168a2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 20:43:21 +0000 Subject: Fix resolving local bdaddr for incoming connections --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index bbb4314c..0c4a16cf 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2097,7 +2097,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) debug("AVDTP: incoming connect from %s", address); size = sizeof(struct sockaddr_l2); - if (getsockname(srv_sk, (struct sockaddr *) &addr, &size) < 0) { + if (getsockname(cli_sk, (struct sockaddr *) &addr, &size) < 0) { error("getsockname: %s (%d)", strerror(errno), errno); close(cli_sk); return TRUE; -- cgit From 9928a07a995d67f038f748cd05bec5147a9f3de2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 22:04:43 +0000 Subject: avdtp_get_internal should not increase the refcount --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 0c4a16cf..387c74e5 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1673,7 +1673,7 @@ static struct avdtp *avdtp_get_internal(bdaddr_t *src, bdaddr_t *dst) session = find_session(src, dst); if (session) - return avdtp_ref(session); + return session; session = g_new0(struct avdtp, 1); -- cgit From ff7907b68ba788c687985a78810fd26d46f551f2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 11 Aug 2007 23:59:24 +0000 Subject: Update CreateDevice method to only take one parameter --- audio/audio-api.txt | 6 +-- audio/manager.c | 122 +++------------------------------------------------- 2 files changed, 9 insertions(+), 119 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index cd736802..d5f587c4 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -12,7 +12,7 @@ org.bluez.audio.Manager interface Object path /org/bluez/audio Methods - string CreateDevice(string address, array{string} interfaces) [experimental] + string CreateDevice(string address) [experimental] Creates a new audio device object. If not yet done, this method will perform a SDP query on the remote @@ -20,9 +20,7 @@ Methods so be sure to call this method asynchronously. The return parameter is the object path of the newly - created object. The method will fail if the remote - device does not support all of the interfaces listed - in the interfaces parameter. + created object. void RemoveDevice(string path) [experimental] diff --git a/audio/manager.c b/audio/manager.c index 87b3b4f3..995cdde5 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -261,42 +261,9 @@ static void handle_record(sdp_record_t *record, struct device *device) device_store(device, is_default); } -static gint record_iface_cmp(gconstpointer a, gconstpointer b) -{ - const sdp_record_t *record = a; - const char *interface = b; - - switch (get_service_uuid(record)) { - case HEADSET_SVCLASS_ID: - case HANDSFREE_SVCLASS_ID: - return strcmp(interface, AUDIO_HEADSET_INTERFACE); - - case HEADSET_AGW_SVCLASS_ID: - case HANDSFREE_AGW_SVCLASS_ID: - return strcmp(interface, AUDIO_GATEWAY_INTERFACE); - - case AUDIO_SINK_SVCLASS_ID: - return strcmp(interface, AUDIO_SINK_INTERFACE); - - case AUDIO_SOURCE_SVCLASS_ID: - return strcmp(interface, AUDIO_SOURCE_INTERFACE); - - case AV_REMOTE_SVCLASS_ID: - return strcmp(interface, AUDIO_CONTROL_INTERFACE); - - case AV_REMOTE_TARGET_SVCLASS_ID: - return strcmp(interface, AUDIO_TARGET_INTERFACE); - - default: - return -1; - } -} - static void finish_sdp(struct audio_sdp_data *data, gboolean success) { const char *addr; - char **required = NULL; - int required_len, i; DBusMessage *reply = NULL; DBusError derr; @@ -312,37 +279,9 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto update; dbus_error_init(&derr); - if (dbus_message_is_method_call(data->msg, AUDIO_MANAGER_INTERFACE, - "CreateHeadset")) { - dbus_message_get_args(data->msg, &derr, - DBUS_TYPE_STRING, &addr, - DBUS_TYPE_INVALID); - required = dbus_new0(char *, 2); - if (required == NULL) { - success = FALSE; - err_failed(connection, data->msg, "Out of memory"); - goto done; - } - - required[0] = dbus_new0(char, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - if (required[0] == NULL) { - success = FALSE; - err_failed(connection, data->msg, "Out of memory"); - goto done; - } - - memcpy(required[0], AUDIO_HEADSET_INTERFACE, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - required[1] = NULL; - required_len = 1; - } - else - dbus_message_get_args(data->msg, &derr, - DBUS_TYPE_STRING, &addr, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &required, &required_len, - DBUS_TYPE_INVALID); + dbus_message_get_args(data->msg, &derr, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { error("Unable to get message args"); @@ -359,18 +298,6 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto done; } - for (i = 0; i < required_len; i++) { - const char *iface = required[i]; - - if (g_slist_find_custom(data->records, iface, record_iface_cmp)) - continue; - - debug("Required interface %s not supported", iface); - success = FALSE; - err_not_supported(connection, data->msg); - goto done; - } - reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; @@ -405,7 +332,6 @@ update: } done: - dbus_free_string_array(required); if (!success) remove_device(data->device); if (data->msg) @@ -753,41 +679,15 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, void *data) { const char *address, *path; - char **required; - int required_len; bdaddr_t bda; struct device *device; DBusMessage *reply; DBusError derr; dbus_error_init(&derr); - if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, - "CreateHeadset")) { - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); - required = dbus_new0(char *, 2); - if (required == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - required[0] = dbus_new0(char, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - if (required[0] == NULL) { - dbus_free(required); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - memcpy(required[0], AUDIO_HEADSET_INTERFACE, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - required[1] = NULL; - required_len = 1; - } - else - dbus_message_get_args(msg, &derr, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &required, &required_len, - DBUS_TYPE_INVALID); + dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { err_invalid_args(connection, msg, derr.message); @@ -800,17 +700,9 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, device = find_device(&bda); if (!device) { device = create_device(&bda); - dbus_free_string_array(required); return resolve_services(msg, device); } - if (!device_matches(device, required)) { - dbus_free_string_array(required); - return err_not_supported(conn, msg); - } - - dbus_free_string_array(required); - path = device->path; reply = dbus_message_new_method_return(msg); @@ -1089,7 +981,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, static DBusMethodVTable manager_methods[] = { { "CreateDevice", am_create_device, - "sas", "s" }, + "s", "s" }, { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, -- cgit From fa90ffdf3ed9cf76ac71b32cda782fb43b6ccbab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 01:33:26 +0000 Subject: Fix (yet again) refcounting. --- audio/avdtp.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 387c74e5..fd99153d 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -288,6 +288,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, struct avdtp_header *header, int size); static int process_queue(struct avdtp *session); +static void connection_lost(struct avdtp *session, int err); static const char *avdtp_statestr(avdtp_state_t state) { @@ -356,7 +357,7 @@ static gboolean disconnect_timeout(gpointer user_data) session->dc_timer = 0; - avdtp_unref(session); + connection_lost(session, -ETIMEDOUT); return FALSE; } @@ -450,7 +451,7 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_IDLE); } -static void connection_lost(struct avdtp *session) +static void connection_lost(struct avdtp *session, int err) { if (session->state == AVDTP_SESSION_STATE_CONNECTED) { char address[18]; @@ -459,8 +460,7 @@ static void connection_lost(struct avdtp *session) debug("Disconnected from %s", address); } - if (session->discov_cb) - finalize_discovery(session, -ECONNABORTED); + finalize_discovery(session, err); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; @@ -476,6 +476,11 @@ static void connection_lost(struct avdtp *session) g_source_remove(session->io); session->io = 0; } + + if (session->ref != 1) + error("connection_lost: ref count not 1 after all callbacks"); + + avdtp_unref(session); } void avdtp_unref(struct avdtp *session) @@ -506,8 +511,6 @@ void avdtp_unref(struct avdtp *session) if (session->dc_timer) remove_disconnect_timer(session); - connection_lost(session); - sessions = g_slist_remove(sessions, session); if (session->req) @@ -1007,8 +1010,7 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: - connection_lost(session); - avdtp_unref(session); + connection_lost(session, -EIO); return FALSE; } @@ -1085,11 +1087,8 @@ failed: avdtp_sep_set_state(session, session->pending_open->lsep, AVDTP_STATE_IDLE); session->pending_open = NULL; - } else { - finalize_discovery(session, -err); - connection_lost(session); - avdtp_unref(session); - } + } else + connection_lost(session, -err); return FALSE; } @@ -1218,8 +1217,7 @@ static gboolean request_timeout(gpointer user_data) goto done; failed: - connection_lost(session); - avdtp_unref(session); + connection_lost(session, -ETIMEDOUT); done: pending_req_free(req); return FALSE; -- cgit From b775db77fe965b6b1768d75198fc9dd201e83818 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 13:11:19 +0000 Subject: l2cap_connect doesn't have to call finalize_discovery as the caller takes care of it (when getting a failure return value) --- audio/avdtp.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index fd99153d..c3bbccc6 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1132,7 +1132,6 @@ static int l2cap_connect(struct avdtp *session) if (!(errno == EAGAIN || errno == EINPROGRESS)) { error("Connect failed. %s(%d)", strerror(errno), errno); - finalize_discovery(session, errno); g_io_channel_close(io); g_io_channel_unref(io); return -errno; -- cgit From 0e46105aa473b7bdc4dfb80b0a0f7858404d94da Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 13:31:27 +0000 Subject: Remove incorrect dbus_message_unref (pending_connect_free takes care of it already) --- audio/sink.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index a660556d..ef31f89b 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -246,7 +246,6 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, err = avdtp_discover(sink->session, discovery_complete, data); if (err < 0) { - dbus_message_unref(c->msg); pending_connect_free(c); sink->c = NULL; return err_connect_failed(conn, msg, strerror(err)); -- cgit From a68d448170540639afaa8d8ff21e581c664afd36 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 14:39:47 +0000 Subject: avdtp_unref improvements --- audio/avdtp.c | 31 +++++++++++++++++++++---------- audio/sink.c | 2 ++ 2 files changed, 23 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index c3bbccc6..ef04126c 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -479,8 +479,8 @@ static void connection_lost(struct avdtp *session, int err) if (session->ref != 1) error("connection_lost: ref count not 1 after all callbacks"); - - avdtp_unref(session); + else + avdtp_unref(session); } void avdtp_unref(struct avdtp *session) @@ -495,19 +495,26 @@ void avdtp_unref(struct avdtp *session) session->ref--; - debug("avdtp_unref: ref=%d", session->ref); + debug("avdtp_unref(%p): ref=%d", session, session->ref); if (session->ref == 1) { - if (session->dc_timer) - remove_disconnect_timer(session); - if (session->sock >= 0) + if (session->state == AVDTP_SESSION_STATE_CONNECTING) { + close(session->sock); + session->sock = -1; + } + + if (session->sock >= 0) set_disconnect_timer(session); - return; + else /* Drop the local ref if we aren't connected */ + session->ref--; } if (session->ref > 0) return; + debug("avdtp_unref(%p): freeing session and removing from list", + session); + if (session->dc_timer) remove_disconnect_timer(session); @@ -527,7 +534,7 @@ void avdtp_unref(struct avdtp *session) struct avdtp *avdtp_ref(struct avdtp *session) { session->ref++; - debug("avdtp_ref: ref=%d", session->ref); + debug("avdtp_ref(%p): ref=%d", session, session->ref); if (session->dc_timer) remove_disconnect_timer(session); return session; @@ -1066,7 +1073,6 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, } if (session->state == AVDTP_SESSION_STATE_CONNECTING) { - session->sock = sk; session->mtu = l2o.imtu; session->buf = g_malloc0(session->mtu); session->state = AVDTP_SESSION_STATE_CONNECTED; @@ -1136,11 +1142,16 @@ static int l2cap_connect(struct avdtp *session) g_io_channel_unref(io); return -errno; } + g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) l2cap_connect_cb, session); - if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) + if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { + session->sock = sk; session->state = AVDTP_SESSION_STATE_CONNECTING; + } + + } else l2cap_connect_cb(io, G_IO_OUT, session); diff --git a/audio/sink.c b/audio/sink.c index ef31f89b..f8d56793 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -248,6 +248,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, if (err < 0) { pending_connect_free(c); sink->c = NULL; + avdtp_unref(sink->session); + sink->session = NULL; return err_connect_failed(conn, msg, strerror(err)); } -- cgit From 05f4ffa36148aebd25d01f12999d5453babe4543 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 16:30:35 +0000 Subject: Fix DISCOVER_CMD response --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ef04126c..650856d4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -617,7 +617,7 @@ static gboolean avdtp_discover_cmd(struct avdtp *session, if (rsp_size + sizeof(struct seid_info) > session->mtu) break; - memcpy(&info, &sep->info, sizeof(struct seid_info)); + memcpy(info, &sep->info, sizeof(struct seid_info)); rsp_size += sizeof(struct seid_info); info++; } -- cgit From 6b51b1ff241a51297f9de35d594b4fcd6c5df1aa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 16:36:13 +0000 Subject: Fix set_configuration reject response --- audio/avdtp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 650856d4..ba6b58ad 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -694,6 +694,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, if (!lsep || !lsep->stream) { init_response(&rej.header, &req->header, FALSE); rej.error = AVDTP_BAD_ACP_SEID; + rej.category = 0x00; /* 0x00 means "not applicable" */ return avdtp_send(session, &rej, sizeof(rej)); } -- cgit From ffdc158c69f7aa3a0e8ce4a67c1a42da33b19c2b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 16:57:34 +0000 Subject: Fix set_configuration command error checking --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ba6b58ad..ec20958c 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -691,7 +691,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, } lsep = find_local_sep_by_seid(req->acp_seid); - if (!lsep || !lsep->stream) { + if (!lsep || lsep->stream) { init_response(&rej.header, &req->header, FALSE); rej.error = AVDTP_BAD_ACP_SEID; rej.category = 0x00; /* 0x00 means "not applicable" */ -- cgit From fb58bd1b24d257ea884aa5d67f8e5a6362aafa94 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 17:03:11 +0000 Subject: Fix set_configuration reject response error codes --- audio/avdtp.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ec20958c..07ca7d42 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -684,6 +684,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, struct setconf_resp *rsp = (struct setconf_resp *) session->buf; struct avdtp_local_sep *lsep; gboolean ret; + uint8_t err; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); @@ -691,11 +692,14 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, } lsep = find_local_sep_by_seid(req->acp_seid); - if (!lsep || lsep->stream) { - init_response(&rej.header, &req->header, FALSE); - rej.error = AVDTP_BAD_ACP_SEID; - rej.category = 0x00; /* 0x00 means "not applicable" */ - return avdtp_send(session, &rej, sizeof(rej)); + if (!lsep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (lsep->stream) { + err = AVDTP_SEP_IN_USE; + goto failed; } init_response(&rsp->header, &req->header, TRUE); @@ -706,6 +710,12 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, avdtp_sep_set_state(session, lsep, AVDTP_STATE_CONFIGURED); return ret; + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = err; + rej.category = 0x00; /* 0x00 means "not applicable" */ + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_getconf_cmd(struct avdtp *session, struct seid_req *req, -- cgit From f817fefff69cc44d5a2e1ba7d8ed9976fe3b4235 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 21:34:50 +0000 Subject: Implement proper acceptor side functionality --- audio/a2dp.c | 30 ++++-- audio/avdtp.c | 306 +++++++++++++++++++++++++++++++++++++++++--------------- audio/avdtp.h | 9 +- audio/manager.c | 77 ++++++++++---- audio/manager.h | 2 +- audio/sink.c | 32 +++++- audio/sink.h | 3 +- 7 files changed, 342 insertions(+), 117 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index abd348c3..6d9009a8 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -36,6 +36,7 @@ #include "logging.h" #include "manager.h" #include "avdtp.h" +#include "sink.h" #include "a2dp.h" static DBusConnection *connection = NULL; @@ -46,15 +47,32 @@ static uint32_t source_record_id = 0; static struct avdtp_local_sep *sink_sep = NULL; static struct avdtp_local_sep *source_sep = NULL; -static gboolean setconf_ind(struct avdtp_local_sep *sep, +static gboolean setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, struct avdtp_stream *stream, - uint8_t int_seid, GSList *caps, - uint8_t *err) + GSList *caps, uint8_t *err, + uint8_t *category) { - if (sep == sink_sep) + struct device *dev; + bdaddr_t addr; + + if (sep == sink_sep) { debug("SBC Sink: Set_Configuration_Ind"); - else - debug("SBC Source: Set_Configuration_Ind"); + return TRUE; + } + + debug("SBC Source: Set_Configuration_Ind"); + + avdtp_get_peers(session, NULL, &addr); + + dev = manager_device_connected(&addr, A2DP_SOURCE_UUID); + if (!dev) { + *err = AVDTP_UNSUPPORTED_CONFIGURATION; + *category = 0x00; + return FALSE; + } + + sink_new_stream(session, stream, dev); return TRUE; } diff --git a/audio/avdtp.c b/audio/avdtp.c index 07ca7d42..30e72761 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -175,6 +175,10 @@ struct close_resp { struct avdtp_header header; } __attribute__ ((packed)); +struct open_resp { + struct avdtp_header header; +} __attribute__ ((packed)); + struct stream_pause_resp { struct avdtp_header header; uint8_t rfa0:2; @@ -221,12 +225,14 @@ struct avdtp_stream { uint16_t mtu; struct avdtp *session; struct avdtp_local_sep *lsep; - struct avdtp_remote_sep *rsep; + uint8_t rseid; GSList *caps; + struct avdtp_service_capability *codec; avdtp_stream_state_cb cb; void *user_data; guint io; /* Transport GSource ID */ - guint close_timer; /* Waiting for other side to close transport */ + guint timer; /* Waiting for other side to close or open + the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ }; @@ -234,6 +240,7 @@ struct avdtp_stream { /* Structure describing an AVDTP connection between two devices */ struct avdtp { int ref; + int free_lock; bdaddr_t src; bdaddr_t dst; @@ -344,11 +351,30 @@ static gboolean stream_close_timeout(gpointer user_data) { struct avdtp_stream *stream = user_data; + debug("Timed out waiting for peer to close the transport channel"); + + stream->timer = 0; + close(stream->sock); return FALSE; } +static gboolean stream_open_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + debug("Timed out waiting for peer to open the transport channel"); + + stream->timer = 0; + + stream->session->pending_open = NULL; + + avdtp_abort(stream->session, stream); + + return FALSE; +} + static gboolean disconnect_timeout(gpointer user_data) { struct avdtp *session = user_data; @@ -390,13 +416,48 @@ static void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) } } +static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, + uint8_t rseid) +{ + GSList *l; + + for (l = session->streams; l != NULL; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->rseid == rseid) + return stream; + } + + return NULL; +} + +static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) +{ + GSList *l; + + for (l = seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == seid) + return sep; + } + + return NULL; +} + static void stream_free(struct avdtp_stream *stream) { + struct avdtp_remote_sep *rsep; + stream->lsep->info.inuse = 0; stream->lsep->stream = NULL; - stream->rsep->stream = NULL; - if (stream->close_timer) - g_source_remove(stream->close_timer); + + rsep = find_remote_sep(stream->session->seps, stream->rseid); + if (rsep) + rsep->stream = NULL; + + if (stream->timer) + g_source_remove(stream->timer); if (stream->caps) { g_slist_foreach(stream->caps, (GFunc) g_free, NULL); g_slist_free(stream->caps); @@ -427,6 +488,8 @@ static void avdtp_sep_set_state(struct avdtp *session, if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); stream_free(stream); + if (session->ref == 1 && !session->streams) + set_disconnect_timer(session); } } @@ -460,11 +523,15 @@ static void connection_lost(struct avdtp *session, int err) debug("Disconnected from %s", address); } + session->free_lock = 1; + finalize_discovery(session, err); g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; + session->free_lock = 0; + if (session->sock >= 0) { close(session->sock); session->sock = -1; @@ -505,7 +572,8 @@ void avdtp_unref(struct avdtp *session) if (session->sock >= 0) set_disconnect_timer(session); - else /* Drop the local ref if we aren't connected */ + else if (!session->free_lock) /* Drop the local ref if we + aren't connected */ session->ref--; } @@ -574,6 +642,42 @@ static struct avdtp_local_sep *find_local_sep(uint8_t type, uint8_t media_type, return NULL; } +static GSList *caps_to_list(uint8_t *data, int size, + struct avdtp_service_capability **codec) +{ + GSList *caps; + int processed; + + for (processed = 0, caps = NULL; processed + 2 < size;) { + struct avdtp_service_capability *cap; + uint8_t length, category; + + category = data[0]; + length = data[1]; + + if (processed + 2 + length > size) { + error("Invalid capability data in getcap resp"); + break; + } + + cap = g_malloc(sizeof(struct avdtp_service_capability) + + length); + memcpy(cap, data, 2 + length); + + processed += 2 + length; + data += 2 + length; + + caps = g_slist_append(caps, cap); + + if (category == AVDTP_MEDIA_CODEC && + length >= + sizeof(struct avdtp_media_codec_capability)) + *codec = cap; + } + + return caps; +} + static void init_response(struct avdtp_header *rsp, struct avdtp_header *req, gboolean accept) { @@ -682,39 +786,62 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, { struct conf_rej rej; struct setconf_resp *rsp = (struct setconf_resp *) session->buf; - struct avdtp_local_sep *lsep; - gboolean ret; - uint8_t err; + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err, category = 0x00; if (size < sizeof(struct setconf_req)) { error("Too short getcap request"); return FALSE; } - lsep = find_local_sep_by_seid(req->acp_seid); - if (!lsep) { + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { err = AVDTP_BAD_ACP_SEID; goto failed; } - if (lsep->stream) { + if (sep->stream) { err = AVDTP_SEP_IN_USE; goto failed; } + + stream = g_new0(struct avdtp_stream, 1); + stream->session = session; + stream->lsep = sep; + stream->rseid = req->int_seid; + stream->caps = caps_to_list(req->caps, + size - sizeof(struct setconf_req), + &stream->codec); + stream->sock = -1; + + if (sep->ind && sep->ind->set_configuration) { + if (!sep->ind->set_configuration(session, sep, stream, + stream->caps, &err, + &category)) { + stream_free(stream); + goto failed; + } + } init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct setconf_req)); + if (!avdtp_send(session, rsp, sizeof(struct setconf_req))) { + stream_free(stream); + return FALSE; + } - if (ret) - avdtp_sep_set_state(session, lsep, AVDTP_STATE_CONFIGURED); + sep->stream = stream; + session->streams = g_slist_append(session->streams, stream); - return ret; + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + + return TRUE; failed: init_response(&rej.header, &req->header, FALSE); rej.error = err; - rej.category = 0x00; /* 0x00 means "not applicable" */ + rej.category = category; return avdtp_send(session, &rej, sizeof(rej)); } @@ -733,7 +860,52 @@ static gboolean avdtp_reconf_cmd(struct avdtp *session, struct seid_req *req, static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct open_resp *rsp = (struct open_resp *) session->buf; + struct seid_rej rej; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_CONFIGURED) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->open) { + if (!sep->ind->open(sep, stream, &err)) + goto failed; + } + + init_response(&rsp->header, &req->header, TRUE); + + if (!avdtp_send(session, rsp, sizeof(struct open_resp))) + return FALSE; + + stream->open_acp = TRUE; + session->pending_open = stream; + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + stream->timer = g_timeout_add(REQ_TIMEOUT, stream_open_timeout, + stream); + + return TRUE; + +failed: + init_response(&rej.header, &req->header, FALSE); + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_start_cmd(struct avdtp *session, struct seid_req *req, @@ -750,7 +922,6 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, struct close_resp *rsp = (struct close_resp *) session->buf; struct seid_rej rej; uint8_t err; - gboolean ret; if (size < sizeof(struct seid_req)) { error("Too short abort request"); @@ -780,14 +951,13 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct close_resp)); - if (ret == TRUE) { - stream->close_timer = g_timeout_add(REQ_TIMEOUT, - stream_close_timeout, - stream); - } + if (!avdtp_send(session, rsp, sizeof(struct close_resp))) + return FALSE; - return ret; + stream->timer = g_timeout_add(REQ_TIMEOUT, stream_close_timeout, + stream); + + return TRUE; failed: init_response(&rej.header, &req->header, FALSE); @@ -912,10 +1082,15 @@ static void handle_transport_connect(struct avdtp *session, int sock, session->pending_open = NULL; + if (stream->timer) { + g_source_remove(stream->timer); + stream->timer = 0; + } + stream->sock = sock; stream->mtu = mtu; - if (sep->cfm && sep->cfm->open) + if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(sep, stream); channel = g_io_channel_unix_new(stream->sock); @@ -979,6 +1154,9 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, if (session->ref == 1 && !session->streams) set_disconnect_timer(session); + if (session->streams && session->dc_timer) + remove_disconnect_timer(session); + return TRUE; } @@ -1180,20 +1358,6 @@ static void queue_request(struct avdtp *session, struct pending_req *req, session->req_queue = g_slist_append(session->req_queue, req); } -static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) -{ - GSList *l; - - for (l = seps; l != NULL; l = g_slist_next(l)) { - struct avdtp_remote_sep *sep = l->data; - - if (sep->seid == seid) - return sep; - } - - return NULL; -} - static gboolean request_timeout(gpointer user_data) { struct avdtp *session = user_data; @@ -1224,7 +1388,7 @@ static gboolean request_timeout(gpointer user_data) goto failed; } - stream = sep->stream; + stream = find_stream_by_rseid(session, seid); memset(&sreq, 0, sizeof(sreq)); init_request(&sreq.header, AVDTP_ABORT); @@ -1339,9 +1503,7 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, struct getcap_resp *resp, int size) { - int processed; struct avdtp_remote_sep *sep; - unsigned char *ptr; uint8_t seid; /* Check for minimum required packet size includes: @@ -1367,36 +1529,8 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, sep->codec = NULL; } - ptr = resp->caps; - processed = sizeof(struct getcap_resp); - - while (processed + 2 < size) { - struct avdtp_service_capability *cap; - uint8_t length, category; - - category = ptr[0]; - length = ptr[1]; - - if (processed + 2 + length > size) { - error("Invalid capability data in getcap resp"); - return FALSE; - } - - cap = g_malloc(sizeof(struct avdtp_service_capability) + - length); - memcpy(cap, ptr, 2 + length); - - processed += 2 + length; - ptr += 2 + length; - - sep->caps = g_slist_append(sep->caps, cap); - - if (category == AVDTP_MEDIA_CODEC && - length >= - sizeof(struct avdtp_media_codec_capability)) - sep->codec = cap; - - } + sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), + &sep->codec); return TRUE; } @@ -1855,7 +1989,7 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_GET_CONFIGURATION); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1883,7 +2017,7 @@ int avdtp_set_configuration(struct avdtp *session, new_stream->session = session; new_stream->lsep = lsep; - new_stream->rsep = rsep; + new_stream->rseid = rsep->seid; new_stream->caps = caps; /* Calculate total size of request */ @@ -1935,7 +2069,7 @@ int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_GET_CONFIGURATION); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, NULL, &req, sizeof(req)); } @@ -1952,7 +2086,7 @@ int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_OPEN); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1969,7 +2103,7 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_START); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } @@ -1987,7 +2121,7 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_CLOSE); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2009,7 +2143,7 @@ int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_SUSPEND); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2032,7 +2166,7 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_ABORT); - req.acp_seid = stream->rsep->seid; + req.acp_seid = stream->rseid; ret = send_request(session, FALSE, stream, &req, sizeof(req)); if (ret == 0) @@ -2254,6 +2388,14 @@ const char *avdtp_strerror(struct avdtp_error *err) } } +void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst) +{ + if (src) + bacpy(src, &session->src); + if (dst) + bacpy(dst, &session->dst); +} + int avdtp_init(void) { if (avdtp_server) diff --git a/audio/avdtp.h b/audio/avdtp.h index b4af9eb3..9c4f41e9 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -121,10 +121,11 @@ struct avdtp_sep_cfm { struct avdtp_sep_ind { gboolean (*get_capability) (struct avdtp_local_sep *sep, GSList **caps, uint8_t *err); - gboolean (*set_configuration) (struct avdtp_local_sep *lsep, + gboolean (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - uint8_t int_seid, GSList *caps, - uint8_t *err); + GSList *caps, uint8_t *err, + uint8_t *category); gboolean (*get_configuration) (struct avdtp_local_sep *lsep, uint8_t *err); gboolean (*open) (struct avdtp_local_sep *lsep, @@ -194,6 +195,8 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep); const char *avdtp_strerror(struct avdtp_error *err); +void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); + int avdtp_init(void); void avdtp_exit(void); diff --git a/audio/manager.c b/audio/manager.c index 995cdde5..e3c5a564 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -85,6 +85,7 @@ struct audio_sdp_data { static DBusConnection *connection = NULL; +static struct device *default_hs = NULL; static struct device *default_dev = NULL; static GSList *devices = NULL; @@ -132,6 +133,11 @@ static void remove_device(struct device *device) default_dev = NULL; } + if (device == default_hs) { + debug("Removing default headset"); + default_hs = NULL; + } + devices = g_slist_remove(devices, device); dbus_connection_destroy_object_path(connection, device->path); @@ -144,6 +150,9 @@ static gboolean add_device(struct device *device) default_dev = device; } + if (!default_hs && device->headset && !devices) + default_hs = device; + devices = g_slist_append(devices, device); return TRUE; @@ -580,16 +589,13 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, return get_handles(GENERIC_AUDIO_UUID, sdp_data); } -struct device *manager_device_connected(bdaddr_t *bda) +struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) { struct device *device; const char *path; - gboolean created = FALSE; + gboolean headset = FALSE, created = FALSE; device = find_device(bda); - if (device && device->headset) - return device; - if (!device) { device = create_device(bda); if (!add_device(device)) { @@ -599,10 +605,26 @@ struct device *manager_device_connected(bdaddr_t *bda) created = TRUE; } - if (!device->headset) + if (!strcmp(uuid, HSP_AG_UUID) || !strcmp(uuid, HSP_AG_UUID)) { + if (device->headset) + return device; + device->headset = headset_init(device, NULL, 0); - if (!device->headset) + if (!device->headset) + return NULL; + + headset = TRUE; + } + else if (!strcmp(uuid, A2DP_SOURCE_UUID)) { + if (device->sink) + return device; + + device->sink = sink_init(device); + + if (!device->sink) + return NULL; + } else return NULL; path = device->path; @@ -616,17 +638,27 @@ struct device *manager_device_connected(bdaddr_t *bda) resolve_services(NULL, device); } - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "HeadsetCreated", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); + if (headset) + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + if (headset && !default_hs) { + default_hs = device; + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultHeadsetChanged", + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + } if (!default_dev) { default_dev = device; dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, - "DefaultHeadsetChanged", + "DefaultDeviceChanged", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); } @@ -1405,6 +1437,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) struct device *device; DBusMessage *auth; DBusPendingCall *pending; + headset_type_t type; if (cond & G_IO_NVAL) return FALSE; @@ -1425,7 +1458,15 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - device = manager_device_connected(&addr.rc_bdaddr); + if (chan == hs_server) { + type = SVC_HEADSET; + uuid = HSP_AG_UUID; + } else { + type = SVC_HANDSFREE; + uuid = HFP_AG_UUID; + } + + device = manager_device_connected(&addr.rc_bdaddr, uuid); if (!device) { close(cli_sk); return TRUE; @@ -1443,13 +1484,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (chan == hs_server) { - headset_set_type(device, SVC_HEADSET); - uuid = HSP_AG_UUID; - } else { - headset_set_type(device, SVC_HANDSFREE); - uuid = HFP_AG_UUID; - } + headset_set_type(device, type); auth = dbus_message_new_method_call("org.bluez", "/org/bluez", "org.bluez.Database", diff --git a/audio/manager.h b/audio/manager.h index 9fbc4940..0bfcad85 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -46,7 +46,7 @@ void audio_exit(void); uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); int remove_service_record(DBusConnection *conn, uint32_t rec_id); -struct device *manager_device_connected(bdaddr_t *bda); +struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); struct device *manager_default_device(); diff --git a/audio/sink.c b/audio/sink.c index f8d56793..be762e39 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -53,6 +53,7 @@ struct sink { uint8_t state; struct pending_connect *c; DBusConnection *conn; + gboolean initiator; }; static void pending_connect_free(struct pending_connect *c) @@ -87,9 +88,13 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_unref(sink->session); sink->session = NULL; } + sink->stream = NULL; c = sink->c; break; case AVDTP_STATE_CONFIGURED: + if (!sink->initiator) + break; + cmd_err = avdtp_open(sink->session, stream); if (cmd_err < 0) { error("Error on avdtp_open %s (%d)", strerror(-cmd_err), @@ -163,11 +168,10 @@ failed: if (new_state == AVDTP_STATE_IDLE) { avdtp_unref(sink->session); sink->session = NULL; + sink->stream = NULL; } } - - static void discovery_complete(struct avdtp *session, GSList *seps, int err, void *user_data) { @@ -205,6 +209,8 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, goto failed; } + sink->initiator = TRUE; + avdtp_stream_set_cb(session, sink->stream, stream_state_changed, dev); return; @@ -299,7 +305,7 @@ static DBusHandlerResult sink_is_connected(DBusConnection *conn, if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - connected = (sink->state != AVDTP_STATE_IDLE); + connected = (sink->state >= AVDTP_STATE_CONFIGURED); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, DBUS_TYPE_INVALID); @@ -456,3 +462,23 @@ avdtp_state_t sink_get_state(void *device) return sink->state; } + +gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, + void *dev) +{ + struct sink *sink = ((struct device *) (dev))->sink; + + if (sink->stream) + return FALSE; + + if (!sink->session) + sink->session = avdtp_ref(session); + + sink->stream = stream; + sink->initiator = FALSE; + + avdtp_stream_set_cb(session, stream, stream_state_changed, dev); + + return TRUE; +} + diff --git a/audio/sink.h b/audio/sink.h index b14a29a7..45745a19 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -29,10 +29,11 @@ struct sink; struct sink *sink_init(void *device); -void sink_new_stream(void *device, void *lsep); void sink_free(void *device); int sink_get_config(void *device, int sock, struct ipc_packet *req, int pkt_len, struct ipc_data_cfg **rsp, int *fd); gboolean sink_is_active(void *device); void sink_set_state(void *device, avdtp_state_t state); avdtp_state_t sink_get_state(void *device); +gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, + void *dev); -- cgit From b7e278c688f4c1442f33ad574577c56979670415 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 22:31:51 +0000 Subject: Clean up protocol structs --- audio/avdtp.c | 53 +++++++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 30e72761..58903d3b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -98,7 +98,11 @@ typedef struct seid_info { /* packets */ -struct discover_req { +struct gen_req { + struct avdtp_header header; +} __attribute__ ((packed)); + +struct gen_resp { struct avdtp_header header; } __attribute__ ((packed)); @@ -134,10 +138,6 @@ struct setconf_req { uint8_t caps[0]; } __attribute__ ((packed)); -struct setconf_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - struct conf_rej { struct avdtp_header header; uint8_t category; @@ -163,29 +163,6 @@ struct reconf_req { uint8_t caps[0]; } __attribute__ ((packed)); -struct reconf_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - -struct abort_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - -struct close_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - -struct open_resp { - struct avdtp_header header; -} __attribute__ ((packed)); - -struct stream_pause_resp { - struct avdtp_header header; - uint8_t rfa0:2; - uint8_t acp_seid:6; - uint8_t error; -} __attribute__ ((packed)); - struct avdtp_general_rej { uint8_t message_type:2; uint8_t packet_type:2; @@ -704,7 +681,7 @@ static gboolean avdtp_unknown_cmd(struct avdtp *session, } static gboolean avdtp_discover_cmd(struct avdtp *session, - struct discover_req *req, int size) + struct gen_req *req, int size) { GSList *l; struct discover_resp *rsp = (struct discover_resp *) session->buf; @@ -785,7 +762,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, struct setconf_req *req, int size) { struct conf_rej rej; - struct setconf_resp *rsp = (struct setconf_resp *) session->buf; + struct gen_resp *rsp = (struct gen_resp *) session->buf; struct avdtp_local_sep *sep; struct avdtp_stream *stream; uint8_t err, category = 0x00; @@ -862,7 +839,7 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, { struct avdtp_local_sep *sep; struct avdtp_stream *stream; - struct open_resp *rsp = (struct open_resp *) session->buf; + struct gen_resp *rsp = (struct gen_resp *) session->buf; struct seid_rej rej; uint8_t err; @@ -891,7 +868,7 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, init_response(&rsp->header, &req->header, TRUE); - if (!avdtp_send(session, rsp, sizeof(struct open_resp))) + if (!avdtp_send(session, rsp, sizeof(struct gen_resp))) return FALSE; stream->open_acp = TRUE; @@ -919,7 +896,7 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, { struct avdtp_local_sep *sep; struct avdtp_stream *stream; - struct close_resp *rsp = (struct close_resp *) session->buf; + struct gen_resp *rsp = (struct gen_resp *) session->buf; struct seid_rej rej; uint8_t err; @@ -951,7 +928,7 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, init_response(&rsp->header, &req->header, TRUE); - if (!avdtp_send(session, rsp, sizeof(struct close_resp))) + if (!avdtp_send(session, rsp, sizeof(struct gen_resp))) return FALSE; stream->timer = g_timeout_add(REQ_TIMEOUT, stream_close_timeout, @@ -975,7 +952,7 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, int size) { struct avdtp_local_sep *sep; - struct abort_resp *rsp = (struct abort_resp *) session->buf; + struct gen_resp *rsp = (struct gen_resp *) session->buf; struct seid_rej rej; uint8_t err; gboolean ret; @@ -997,7 +974,7 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, } init_response(&rsp->header, &req->header, TRUE); - ret = avdtp_send(session, rsp, sizeof(struct abort_resp)); + ret = avdtp_send(session, rsp, sizeof(struct gen_resp)); if (ret) avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); @@ -1600,7 +1577,7 @@ static gboolean avdtp_close_resp(struct avdtp *session, static gboolean avdtp_suspend_resp(struct avdtp *session, struct avdtp_stream *stream, - struct stream_pause_resp *resp, + struct gen_resp *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; @@ -1912,7 +1889,7 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) { - struct discover_req req; + struct gen_req req; int ret; if (session->discov_cb) -- cgit From f9d8837abad3b67b70425157ac854deb892b2b91 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 22:43:14 +0000 Subject: All local SEP callbacks need a avdtp session parameter --- audio/a2dp.c | 38 ++++++++++++++++++++++---------------- audio/avdtp.c | 20 ++++++++++---------- audio/avdtp.h | 40 ++++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 42 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 6d9009a8..1f34fc24 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -77,7 +77,7 @@ static gboolean setconf_ind(struct avdtp *session, return TRUE; } -static gboolean getcap_ind(struct avdtp_local_sep *sep, +static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err) { struct avdtp_service_capability *media_transport, *media_codec; @@ -131,7 +131,7 @@ static gboolean getcap_ind(struct avdtp_local_sep *sep, return TRUE; } -static void setconf_cfm(struct avdtp_local_sep *sep, +static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { if (sep == sink_sep) @@ -140,7 +140,8 @@ static void setconf_cfm(struct avdtp_local_sep *sep, debug("SBC Source: Set_Configuration_Cfm"); } -static gboolean getconf_ind(struct avdtp_local_sep *sep, uint8_t *err) +static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, + uint8_t *err) { if (sep == sink_sep) debug("SBC Sink: Get_Configuration_Ind"); @@ -149,7 +150,7 @@ static gboolean getconf_ind(struct avdtp_local_sep *sep, uint8_t *err) return TRUE; } -static void getconf_cfm(struct avdtp_local_sep *sep, +static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { if (sep == sink_sep) @@ -158,7 +159,7 @@ static void getconf_cfm(struct avdtp_local_sep *sep, debug("SBC Source: Set_Configuration_Cfm"); } -static gboolean open_ind(struct avdtp_local_sep *sep, +static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { if (sep == sink_sep) @@ -168,7 +169,8 @@ static gboolean open_ind(struct avdtp_local_sep *sep, return TRUE; } -static void open_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream) { if (sep == sink_sep) debug("SBC Sink: Open_Cfm"); @@ -176,7 +178,7 @@ static void open_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) debug("SBC Source: Open_Cfm"); } -static gboolean start_ind(struct avdtp_local_sep *sep, +static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { if (sep == sink_sep) @@ -186,7 +188,8 @@ static gboolean start_ind(struct avdtp_local_sep *sep, return TRUE; } -static void start_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream) { if (sep == sink_sep) debug("SBC Sink: Start_Cfm"); @@ -194,7 +197,7 @@ static void start_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) debug("SBC Source: Start_Cfm"); } -static gboolean suspend_ind(struct avdtp_local_sep *sep, +static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { if (sep == sink_sep) @@ -204,7 +207,7 @@ static gboolean suspend_ind(struct avdtp_local_sep *sep, return TRUE; } -static void suspend_cfm(struct avdtp_local_sep *sep, +static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { if (sep == sink_sep) @@ -213,7 +216,7 @@ static void suspend_cfm(struct avdtp_local_sep *sep, debug("SBC Source: Suspend_Cfm"); } -static gboolean close_ind(struct avdtp_local_sep *sep, +static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { if (sep == sink_sep) @@ -223,7 +226,8 @@ static gboolean close_ind(struct avdtp_local_sep *sep, return TRUE; } -static void close_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream) { if (sep == sink_sep) debug("SBC Sink: Close_Cfm"); @@ -231,7 +235,7 @@ static void close_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) debug("SBC Source: Close_Cfm"); } -static gboolean abort_ind(struct avdtp_local_sep *sep, +static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { if (sep == sink_sep) @@ -241,7 +245,8 @@ static gboolean abort_ind(struct avdtp_local_sep *sep, return TRUE; } -static void abort_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) +static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream) { if (sep == sink_sep) debug("SBC Sink: Abort_Cfm"); @@ -249,7 +254,8 @@ static void abort_cfm(struct avdtp_local_sep *sep, struct avdtp_stream *stream) debug("SBC Source: Abort_Cfm"); } -static gboolean reconf_ind(struct avdtp_local_sep *sep, uint8_t *err) +static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, + uint8_t *err) { if (sep == sink_sep) debug("SBC Sink: ReConfigure_Ind"); @@ -258,7 +264,7 @@ static gboolean reconf_ind(struct avdtp_local_sep *sep, uint8_t *err) return TRUE; } -static void reconf_cfm(struct avdtp_local_sep *sep) +static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep) { if (sep == sink_sep) debug("SBC Sink: ReConfigure_Cfm"); diff --git a/audio/avdtp.c b/audio/avdtp.c index 58903d3b..3dafc1bc 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -728,7 +728,7 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, goto failed; } - if (!sep->ind->get_capability(sep, &caps, &err)) + if (!sep->ind->get_capability(session, sep, &caps, &err)) goto failed; init_response(&rsp->header, &req->header, TRUE); @@ -862,7 +862,7 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->open) { - if (!sep->ind->open(sep, stream, &err)) + if (!sep->ind->open(session, sep, stream, &err)) goto failed; } @@ -920,7 +920,7 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->close) { - if (!sep->ind->close(sep, stream, &err)) + if (!sep->ind->close(session, sep, stream, &err)) goto failed; } @@ -969,7 +969,7 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, } if (sep->ind && sep->ind->abort) { - if (!sep->ind->abort(sep, sep->stream, &err)) + if (!sep->ind->abort(session, sep, sep->stream, &err)) goto failed; } @@ -1043,7 +1043,7 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(sep, stream); + sep->cfm->close(stream->session, sep, stream); avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); @@ -1068,7 +1068,7 @@ static void handle_transport_connect(struct avdtp *session, int sock, stream->mtu = mtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(sep, stream); + sep->cfm->open(session, sep, stream); channel = g_io_channel_unix_new(stream->sock); @@ -1520,7 +1520,7 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(sep, stream); + sep->cfm->set_configuration(session, sep, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); @@ -1554,7 +1554,7 @@ static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->start) - sep->cfm->start(sep, stream); + sep->cfm->start(session, sep, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); @@ -1583,7 +1583,7 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(sep, stream); + sep->cfm->suspend(session, sep, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); @@ -1597,7 +1597,7 @@ static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(sep, stream); + sep->cfm->suspend(session, sep, stream); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); diff --git a/audio/avdtp.h b/audio/avdtp.h index 9c4f41e9..67fb38df 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -99,49 +99,57 @@ typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, /* Callbacks for when a reply is received to a command that we sent */ struct avdtp_sep_cfm { - void (*set_configuration) (struct avdtp_local_sep *lsep, + void (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*get_configuration) (struct avdtp_local_sep *lsep, + void (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*open) (struct avdtp_local_sep *lsep, + void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*start) (struct avdtp_local_sep *lsep, + void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*suspend) (struct avdtp_local_sep *lsep, + void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*close) (struct avdtp_local_sep *lsep, + void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*abort) (struct avdtp_local_sep *lsep, + void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream); - void (*reconfigure) (struct avdtp_local_sep *lsep); + void (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep); }; /* Callbacks for indicating when we received a new command. The return value * indicates whether the command should be rejected or accepted */ struct avdtp_sep_ind { - gboolean (*get_capability) (struct avdtp_local_sep *sep, + gboolean (*get_capability) (struct avdtp *session, + struct avdtp_local_sep *sep, GSList **caps, uint8_t *err); gboolean (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, uint8_t *category); - gboolean (*get_configuration) (struct avdtp_local_sep *lsep, + gboolean (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, uint8_t *err); - gboolean (*open) (struct avdtp_local_sep *lsep, + gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err); - gboolean (*start) (struct avdtp_local_sep *lsep, + gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, uint8_t *err); - gboolean (*suspend) (struct avdtp_local_sep *sep, + gboolean (*suspend) (struct avdtp *session, + struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err); - gboolean (*close) (struct avdtp_local_sep *sep, + gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err); - gboolean (*abort) (struct avdtp_local_sep *sep, + gboolean (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err); - gboolean (*reconfigure) (struct avdtp_local_sep *lsep, uint8_t *err); + gboolean (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err); }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, -- cgit From d013a1eaa7beebbb49c1fe0015c70ad81566d97c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 12 Aug 2007 23:58:15 +0000 Subject: Clean up mess with header files --- audio/a2dp.c | 3 ++- audio/device.c | 2 ++ audio/device.h | 13 +++++++++--- audio/gateway.c | 2 ++ audio/gateway.h | 4 ++-- audio/headset.c | 61 +++++++++++++++++++++++++++------------------------------ audio/headset.h | 32 ++++++++++++------------------ audio/manager.c | 6 +++++- audio/manager.h | 1 + audio/sink.c | 28 +++++++++++--------------- audio/sink.h | 19 +++++++++--------- audio/unix.c | 1 + 12 files changed, 87 insertions(+), 85 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 1f34fc24..03cb8868 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -35,6 +35,7 @@ #include "logging.h" #include "manager.h" +#include "device.h" #include "avdtp.h" #include "sink.h" #include "a2dp.h" @@ -72,7 +73,7 @@ static gboolean setconf_ind(struct avdtp *session, return FALSE; } - sink_new_stream(session, stream, dev); + sink_new_stream(dev, session, stream); return TRUE; } diff --git a/audio/device.c b/audio/device.c index 18e494c1..54d4bb6f 100644 --- a/audio/device.c +++ b/audio/device.c @@ -43,6 +43,8 @@ #include "logging.h" #include "textfile.h" +#include "headset.h" +#include "sink.h" #include "device.h" static DBusHandlerResult device_get_address(DBusConnection *conn, diff --git a/audio/device.h b/audio/device.h index d7608741..5782dd99 100644 --- a/audio/device.h +++ b/audio/device.h @@ -20,12 +20,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#ifndef __AUDIO_DEVICE_H__ +#define __AUDIO_DEVICE_H__ #include +#include +#include -#include "headset.h" -#include "gateway.h" -#include "sink.h" +#include "ipc.h" #define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" @@ -53,6 +55,9 @@ struct source; struct control; struct target; +struct sink; +struct headset; +struct gateway; struct device { DBusConnection *conn; @@ -82,3 +87,5 @@ int device_get_config(struct device *dev, int sock, struct ipc_packet *req, void device_set_state(struct device *dev, uint8_t state); uint8_t device_get_state(struct device *dev); + +#endif diff --git a/audio/gateway.c b/audio/gateway.c index 2f4ab59e..d91a2225 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -24,3 +24,5 @@ #ifdef HAVE_CONFIG_H #include #endif + +#include "gateway.h" diff --git a/audio/gateway.h b/audio/gateway.h index 8794ac99..7fddb33c 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -21,9 +21,9 @@ * */ -#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" +#include "device.h" -struct gateway; +#define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); diff --git a/audio/headset.c b/audio/headset.c index 9c91c925..3a01dbb1 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,9 +51,11 @@ #include "dbus.h" #include "dbus-helper.h" #include "logging.h" +#include "device.h" #include "manager.h" #include "error.h" #include "unix.h" +#include "headset.h" #define RING_INTERVAL 3000 @@ -1291,9 +1293,9 @@ static void headset_set_channel(struct headset *headset, sdp_record_t *record) error("Unable to get RFCOMM channel from Headset record"); } -void headset_update(void *device, sdp_record_t *record, uint16_t svc) +void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) { - struct headset *headset = ((struct device *) device)->headset; + struct headset *headset = dev->headset; switch (svc) { case HANDSFREE_SVCLASS_ID: @@ -1329,10 +1331,9 @@ void headset_update(void *device, sdp_record_t *record, uint16_t svc) headset_set_channel(headset, record); } -struct headset *headset_init(void *device, sdp_record_t *record, - uint16_t svc) +struct headset *headset_init(struct device *dev, sdp_record_t *record, + uint16_t svc) { - struct device *dev = (struct device *) device; struct headset *hs; hs = g_new0(struct headset, 1); @@ -1371,9 +1372,8 @@ register_iface: return hs; } -void headset_free(void *device) +void headset_free(struct device *dev) { - struct device *dev = device; struct headset *hs = dev->headset; if (hs->sco) { @@ -1390,10 +1390,10 @@ void headset_free(void *device) dev->headset = NULL; } -int headset_get_config(void *device, int sock, struct ipc_packet *pkt, +int headset_get_config(struct device *dev, int sock, struct ipc_packet *pkt, int pkt_len, struct ipc_data_cfg **cfg, int *fd) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; int err = EINVAL; struct pending_connect *c; @@ -1406,9 +1406,9 @@ int headset_get_config(void *device, int sock, struct ipc_packet *pkt, memcpy(c->pkt, pkt, pkt_len); if (hs->rfcomm == NULL) - err = rfcomm_connect(device, c); + err = rfcomm_connect(dev, c); else if (hs->sco == NULL) - err = sco_connect(device, c); + err = sco_connect(dev, c); else goto error; @@ -1436,32 +1436,31 @@ error: return -err; } -headset_type_t headset_get_type(void *device) +headset_type_t headset_get_type(struct device *dev) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; return hs->type; } -void headset_set_type(void *device, headset_type_t type) +void headset_set_type(struct device *dev, headset_type_t type) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; hs->type = type; } -int headset_connect_rfcomm(void *device, int sock) +int headset_connect_rfcomm(struct device *dev, int sock) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; hs->rfcomm = g_io_channel_unix_new(sock); return hs->rfcomm ? 0 : -EINVAL; } -int headset_close_rfcomm(void *device) +int headset_close_rfcomm(struct device *dev) { - struct device *dev = (struct device *) device; struct headset *hs = dev->headset; if (hs->ring_timer) { @@ -1480,9 +1479,8 @@ int headset_close_rfcomm(void *device) return 0; } -void headset_set_state(void *device, headset_state_t state) +void headset_set_state(struct device *dev, headset_state_t state) { - struct device *dev = (struct device *) device; struct headset *hs = dev->headset; char str[13]; @@ -1491,8 +1489,8 @@ void headset_set_state(void *device, headset_state_t state) switch(state) { case HEADSET_STATE_DISCONNECTED: - close_sco(device); - headset_close_rfcomm(device); + close_sco(dev); + headset_close_rfcomm(dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Disconnected", @@ -1504,7 +1502,7 @@ void headset_set_state(void *device, headset_state_t state) if (hs->state < state) { g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, device); + (GIOFunc) rfcomm_io_cb, dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, @@ -1512,7 +1510,7 @@ void headset_set_state(void *device, headset_state_t state) DBUS_TYPE_INVALID); } else { - close_sco(device); + close_sco(dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Stopped", @@ -1523,7 +1521,7 @@ void headset_set_state(void *device, headset_state_t state) break; case HEADSET_STATE_PLAYING: g_io_add_watch(hs->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) sco_cb, device); + (GIOFunc) sco_cb, dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, @@ -1548,23 +1546,22 @@ void headset_set_state(void *device, headset_state_t state) hs->state = state; } -headset_state_t headset_get_state(void *device) +headset_state_t headset_get_state(struct device *dev) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; return hs->state; } -int headset_get_channel(void *device) +int headset_get_channel(struct device *dev) { - struct headset *hs = ((struct device *) device)->headset; + struct headset *hs = dev->headset; return hs->rfcomm_ch; } -gboolean headset_is_active(void *device) +gboolean headset_is_active(struct device *dev) { - struct device *dev = device; struct headset *hs = dev->headset; if (hs->state != HEADSET_STATE_DISCONNECTED) diff --git a/audio/headset.h b/audio/headset.h index db1fef65..ec57f1d8 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,15 +20,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AUDIO_HEADSET_H__ -#define __AUDIO_HEADSET_H__ - #include #include #include #include "ipc.h" +#include "device.h" #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" @@ -55,29 +53,25 @@ typedef enum { SVC_HANDSFREE } headset_type_t; -struct headset; - -struct headset *headset_init(void *device, sdp_record_t *record, +struct headset *headset_init(struct device *dev, sdp_record_t *record, uint16_t svc); -void headset_free(void *device); +void headset_free(struct device *dev); -void headset_update(void *device, sdp_record_t *record, uint16_t svc); +void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); -int headset_get_config(void *device, int sock, struct ipc_packet *pkt, +int headset_get_config(struct device *dev, int sock, struct ipc_packet *pkt, int pkt_len, struct ipc_data_cfg **rsp, int *fd); -headset_type_t headset_get_type(void *device); -void headset_set_type(void *device, headset_type_t type); - -int headset_connect_rfcomm(void *device, int sock); -int headset_close_rfcomm(void *device); +headset_type_t headset_get_type(struct device *dev); +void headset_set_type(struct device *dev, headset_type_t type); -headset_state_t headset_get_state(void *device); -void headset_set_state(void *device, headset_state_t state); +int headset_connect_rfcomm(struct device *dev, int sock); +int headset_close_rfcomm(struct device *dev); -int headset_get_channel(void *device); +headset_state_t headset_get_state(struct device *dev); +void headset_set_state(struct device *dev, headset_state_t state); -gboolean headset_is_active(void *device); +int headset_get_channel(struct device *dev); -#endif +gboolean headset_is_active(struct device *dev); diff --git a/audio/manager.c b/audio/manager.c index e3c5a564..dd0907f6 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -50,10 +50,14 @@ #include "dbus-helper.h" #include "logging.h" #include "textfile.h" -#include "manager.h" +#include "device.h" #include "error.h" #include "a2dp.h" #include "avdtp.h" +#include "headset.h" +#include "gateway.h" +#include "sink.h" +#include "manager.h" typedef enum { HEADSET = 1 << 0, diff --git a/audio/manager.h b/audio/manager.h index 0bfcad85..993c1057 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -21,6 +21,7 @@ * */ +#include #include #include "device.h" diff --git a/audio/sink.c b/audio/sink.c index be762e39..08a862cf 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -39,6 +39,7 @@ #include "a2dp.h" #include "error.h" #include "unix.h" +#include "sink.h" struct pending_connect { DBusMessage *msg; @@ -328,10 +329,8 @@ static DBusSignalVTable sink_signals[] = { { NULL, NULL } }; -struct sink *sink_init(void *device) +struct sink *sink_init(struct device *dev) { - struct device *dev = device; - if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_SINK_INTERFACE, sink_methods, @@ -341,9 +340,8 @@ struct sink *sink_init(void *device) return g_new0(struct sink, 1); } -void sink_free(void *device) +void sink_free(struct device *dev) { - struct device *dev = device; struct sink *sink = dev->sink; if (sink->session) @@ -356,10 +354,9 @@ void sink_free(void *device) dev->sink = NULL; } -int sink_get_config(void *device, int sock, struct ipc_packet *req, +int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int pkt_len, struct ipc_data_cfg **rsp, int *fd) { - struct device *dev = device; struct sink *sink = dev->sink; int err = EINVAL; struct pending_connect *c = NULL; @@ -377,7 +374,7 @@ int sink_get_config(void *device, int sock, struct ipc_packet *req, sink->c = c; if (sink->state == AVDTP_STATE_IDLE) - err = avdtp_discover(sink->session, discovery_complete, device); + err = avdtp_discover(sink->session, discovery_complete, dev); else if (sink->state < AVDTP_STATE_STREAMING) err = avdtp_start(sink->session, sink->stream); else @@ -400,9 +397,8 @@ error: return -err; } -gboolean sink_is_active(void *device) +gboolean sink_is_active(struct device *dev) { - struct device *dev = device; struct sink *sink = dev->sink; if (sink->session) @@ -411,9 +407,8 @@ gboolean sink_is_active(void *device) return FALSE; } -void sink_set_state(void *device, avdtp_state_t state) +void sink_set_state(struct device *dev, avdtp_state_t state) { - struct device *dev = device; struct sink *sink = dev->sink; int err = 0; @@ -455,18 +450,17 @@ failed: error("%s: Error changing states", dev->path); } -avdtp_state_t sink_get_state(void *device) +avdtp_state_t sink_get_state(struct device *dev) { - struct device *dev = device; struct sink *sink = dev->sink; return sink->state; } -gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, - void *dev) +gboolean sink_new_stream(struct device *dev, struct avdtp *session, + struct avdtp_stream *stream) { - struct sink *sink = ((struct device *) (dev))->sink; + struct sink *sink = dev->sink; if (sink->stream) return FALSE; diff --git a/audio/sink.h b/audio/sink.h index 45745a19..4cb5d0c3 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -23,17 +23,16 @@ #include "ipc.h" #include "avdtp.h" +#include "device.h" #define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" -struct sink; - -struct sink *sink_init(void *device); -void sink_free(void *device); -int sink_get_config(void *device, int sock, struct ipc_packet *req, +struct sink *sink_init(struct device *dev); +void sink_free(struct device *dev); +int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int pkt_len, struct ipc_data_cfg **rsp, int *fd); -gboolean sink_is_active(void *device); -void sink_set_state(void *device, avdtp_state_t state); -avdtp_state_t sink_get_state(void *device); -gboolean sink_new_stream(struct avdtp *session, struct avdtp_stream *stream, - void *dev); +gboolean sink_is_active(struct device *dev); +void sink_set_state(struct device *dev, avdtp_state_t state); +avdtp_state_t sink_get_state(struct device *dev); +gboolean sink_new_stream(struct device *dev, struct avdtp *session, + struct avdtp_stream *stream); diff --git a/audio/unix.c b/audio/unix.c index 355a7403..8a65df8e 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -37,6 +37,7 @@ #include "logging.h" #include "dbus.h" +#include "device.h" #include "manager.h" #include "ipc.h" #include "unix.h" -- cgit From c2833e263d6cfc4cf82f4bfdcc59640a4071aeae Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 08:14:22 +0000 Subject: Remove ifndef protections and includes from .h files --- audio/a2dp.c | 2 ++ audio/a2dp.h | 4 ---- audio/avdtp.c | 8 ++++++-- audio/avdtp.h | 6 ------ audio/ctl_bluetooth.c | 2 ++ audio/device.c | 6 +++++- audio/device.h | 10 ---------- audio/error.c | 4 ++++ audio/error.h | 2 -- audio/gateway.c | 5 +++++ audio/gateway.h | 2 -- audio/headset.c | 1 + audio/headset.h | 7 ------- audio/ipc.h | 9 ++------- audio/main.c | 5 ++++- audio/manager.c | 4 +++- audio/manager.h | 5 ----- audio/sink.c | 4 ++++ audio/sink.h | 4 ---- audio/unix.c | 5 ++++- audio/unix.h | 2 -- 21 files changed, 42 insertions(+), 55 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 03cb8868..fd355302 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -30,11 +30,13 @@ #include #include +#include #include #include #include "logging.h" #include "manager.h" +#include "ipc.h" #include "device.h" #include "avdtp.h" #include "sink.h" diff --git a/audio/a2dp.h b/audio/a2dp.h index 708f9f94..b2653594 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -21,10 +21,6 @@ * */ -#include -#include -#include "avdtp.h" - #define A2DP_CODEC_SBC 0x00 #define A2DP_CODEC_MPEG12 0x01 #define A2DP_CODEC_MPEG24 0x02 diff --git a/audio/avdtp.c b/audio/avdtp.c index 3dafc1bc..febef897 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -26,6 +26,7 @@ #endif #include +#include #include #include #include @@ -34,9 +35,12 @@ #include -#include "avdtp.h" -#include "logging.h" +#include + #include "dbus.h" +#include "logging.h" + +#include "avdtp.h" #include diff --git a/audio/avdtp.h b/audio/avdtp.h index 67fb38df..f7ca6308 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -20,11 +20,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AVDTP_H__ -#define __AVDTP_H__ - -#include -#include struct avdtp; struct avdtp_stream; @@ -208,4 +203,3 @@ void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); int avdtp_init(void); void avdtp_exit(void); -#endif diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 411d7e00..9e1c320c 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -31,6 +31,8 @@ #include #include +#include + #include "ipc.h" #ifdef ENABLE_DEBUG diff --git a/audio/device.c b/audio/device.c index 54d4bb6f..119a4e4f 100644 --- a/audio/device.c +++ b/audio/device.c @@ -37,15 +37,19 @@ #include #include #include +#include +#include #include "dbus.h" #include "dbus-helper.h" #include "logging.h" #include "textfile.h" +#include "ipc.h" +#include "device.h" +#include "avdtp.h" #include "headset.h" #include "sink.h" -#include "device.h" static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *msg, void *data) diff --git a/audio/device.h b/audio/device.h index 5782dd99..80a95906 100644 --- a/audio/device.h +++ b/audio/device.h @@ -20,14 +20,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AUDIO_DEVICE_H__ -#define __AUDIO_DEVICE_H__ - -#include -#include -#include - -#include "ipc.h" #define AUDIO_DEVICE_INTERFACE "org.bluez.audio.Device" @@ -87,5 +79,3 @@ int device_get_config(struct device *dev, int sock, struct ipc_packet *req, void device_set_state(struct device *dev, uint8_t state); uint8_t device_get_state(struct device *dev); - -#endif diff --git a/audio/error.c b/audio/error.c index 275eb2a7..3bec2f61 100644 --- a/audio/error.c +++ b/audio/error.c @@ -25,6 +25,10 @@ #include #endif +#include + +#include "dbus.h" + #include "error.h" #include "logging.h" diff --git a/audio/error.h b/audio/error.h index de4e8982..712ee20d 100644 --- a/audio/error.h +++ b/audio/error.h @@ -21,8 +21,6 @@ * */ -#include "dbus.h" - DBusHandlerResult err_invalid_args(DBusConnection *conn, DBusMessage *msg, const char *descr); DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); diff --git a/audio/gateway.c b/audio/gateway.c index d91a2225..66db457f 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -25,4 +25,9 @@ #include #endif +#include + +#include +#include + #include "gateway.h" diff --git a/audio/gateway.h b/audio/gateway.h index 7fddb33c..572457c9 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -21,8 +21,6 @@ * */ -#include "device.h" - #define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); diff --git a/audio/headset.c b/audio/headset.c index 3a01dbb1..b0e67ba2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,6 +51,7 @@ #include "dbus.h" #include "dbus-helper.h" #include "logging.h" +#include "ipc.h" #include "device.h" #include "manager.h" #include "error.h" diff --git a/audio/headset.h b/audio/headset.h index ec57f1d8..2a65d136 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -20,13 +20,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#include -#include - -#include - -#include "ipc.h" -#include "device.h" #define AUDIO_HEADSET_INTERFACE "org.bluez.audio.Headset" diff --git a/audio/ipc.h b/audio/ipc.h index 473efc05..77997496 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -20,10 +20,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#ifndef __AUDIO_IPC_H__ -#define __AUDIO_IPC_H__ - -#include #define IPC_TYPE_CONNECT 0x0001 @@ -78,7 +74,8 @@ struct ipc_packet { #define CFG_CODEC_SBC 1 struct ipc_data_cfg { - uint8_t fd_opt; /* Stream file descriptor options: read, write or readwrite*/ + uint8_t fd_opt; /* Stream file descriptor options: read, + write or readwrite */ uint8_t channels; /* Number of audio channel */ uint8_t channel_mode; /* Audio channel mode*/ uint16_t pkt_len; /* Stream packet length */ @@ -133,5 +130,3 @@ struct ipc_data_ctl { uint8_t mode; /* Control Mode */ uint8_t key; /* Control Key */ } __attribute__ ((packed)); - -#endif diff --git a/audio/main.c b/audio/main.c index 72e54133..9e677354 100644 --- a/audio/main.c +++ b/audio/main.c @@ -26,15 +26,18 @@ #endif #include +#include #include #include #include - #include +#include +#include #include "dbus.h" #include "logging.h" +#include "ipc.h" #include "unix.h" #include "manager.h" diff --git a/audio/manager.c b/audio/manager.c index dd0907f6..7a650054 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -48,12 +48,14 @@ #include #include "dbus-helper.h" +#include "dbus.h" #include "logging.h" #include "textfile.h" +#include "ipc.h" #include "device.h" #include "error.h" -#include "a2dp.h" #include "avdtp.h" +#include "a2dp.h" #include "headset.h" #include "gateway.h" #include "sink.h" diff --git a/audio/manager.h b/audio/manager.h index 993c1057..a18cf4fd 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -21,11 +21,6 @@ * */ -#include -#include - -#include "device.h" - #define MAX_PATH_LENGTH 64 /* D-Bus path */ #define AUDIO_MANAGER_PATH "/org/bluez/audio" #define AUDIO_MANAGER_INTERFACE "org.bluez.audio.Manager" diff --git a/audio/sink.c b/audio/sink.c index 08a862cf..155170e6 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -25,16 +25,20 @@ #include #endif +#include #include #include #include +#include + #include "dbus.h" #include "dbus-helper.h" #include "logging.h" #include "avdtp.h" +#include "ipc.h" #include "device.h" #include "a2dp.h" #include "error.h" diff --git a/audio/sink.h b/audio/sink.h index 4cb5d0c3..7894a36c 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -21,10 +21,6 @@ * */ -#include "ipc.h" -#include "avdtp.h" -#include "device.h" - #define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" struct sink *sink_init(struct device *dev); diff --git a/audio/unix.c b/audio/unix.c index 8a65df8e..d977633c 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -33,13 +33,16 @@ #include #include +#include +#include +#include #include #include "logging.h" #include "dbus.h" +#include "ipc.h" #include "device.h" #include "manager.h" -#include "ipc.h" #include "unix.h" struct unix_client { diff --git a/audio/unix.h b/audio/unix.h index 83e2518a..7f42688c 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -21,8 +21,6 @@ * */ -#include "ipc.h" - int unix_init(void); void unix_exit(void); -- cgit From 6e1dd6ef0f595cf51204545e2aa5d6b90225fee4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 13 Aug 2007 10:32:27 +0000 Subject: Fix compilation of debug only sections --- audio/pcm_bluetooth.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b5f74c94..62d77fe5 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -40,7 +40,7 @@ #include "ipc.h" #include "sbc.h" -/*#define ENABLE_DEBUG */ +//#define ENABLE_DEBUG #define BUFFER_SIZE 1024 @@ -190,7 +190,7 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params uint32_t period_count = io->buffer_size / io->period_size; int opt_name, err; - DBG("fd = %d, period_count = %d", cfg.fd, period_count); + DBG("fd = %d, period_count = %d", data->stream_fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; @@ -380,11 +380,11 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) real = (long) (sendz_delay.tv_sec * 1000 + sendz_delay.tv_usec / 1000); theo = (long) (((float) a2dp->nsamples) / - cfg->rate * 1000.0); + data->cfg.rate * 1000.0); delta = (long) (sendz_delay.tv_sec * 1000 + sendz_delay.tv_usec / 1000) - (long) (((float) a2dp->nsamples) / - cfg->rate * 1000.0); + data->cfg.rate * 1000.0); timersub(&send_date, &prev_date, &send_delay); timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); @@ -765,7 +765,7 @@ done: DBG("Device configuration:"); DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n" - "\tsample_size=%u\n\trate=%u", data->cfg.fd, + "\tsample_size=%u\n\trate=%u", data->stream_fd, data->cfg.fd_opt, data->cfg.channels, data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); -- cgit From 8f0de90a8fd518bc5a1b0c63a6b9d2357375a287 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 13 Aug 2007 17:16:12 +0000 Subject: Fix RemoveDevice bug that prevent its removal from storage. --- audio/device.c | 13 +++++++++++++ audio/device.h | 2 ++ audio/manager.c | 23 +++++++++++++++-------- 3 files changed, 30 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 119a4e4f..450b2afe 100644 --- a/audio/device.c +++ b/audio/device.c @@ -221,6 +221,19 @@ int device_store(struct device *dev, gboolean is_default) return textfile_put(filename, dst_addr, value); } +int device_remove_stored(struct device *dev) +{ + char filename[PATH_MAX + 1]; + char src_addr[18], dst_addr[18]; + + ba2str(&dev->dst, dst_addr); + ba2str(&dev->src, src_addr); + + create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); + + return textfile_del(filename, dst_addr); +} + void device_finish_sdp_transaction(struct device *dev) { char address[18], *addr_ptr = address; diff --git a/audio/device.h b/audio/device.h index 80a95906..d9a42ca1 100644 --- a/audio/device.h +++ b/audio/device.h @@ -71,6 +71,8 @@ struct device *device_register(DBusConnection *conn, int device_store(struct device *device, gboolean is_default); +int device_remove_stored(struct device *dev); + void device_finish_sdp_transaction(struct device *device); int device_get_config(struct device *dev, int sock, struct ipc_packet *req, diff --git a/audio/manager.c b/audio/manager.c index 7a650054..231af376 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -862,20 +862,19 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, return DBUS_HANDLER_RESULT_NEED_MEMORY; device = match->data; - remove_device(device); + device_remove_stored(device); - if (default_dev == device) { + /* Fallback to a valid default */ + if (default_dev == NULL) { const char *param; GSList *l; - default_dev = NULL; - - for (l = devices; l != NULL; l = l->next) { - device = l->data; + default_dev = manager_get_connected_device(); - if (device->headset) - default_dev = device; + if (!default_dev) { + l = devices; + default_dev = (g_slist_last(l))->data; } param = default_dev ? default_dev->path : ""; @@ -885,6 +884,14 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, "DefaultHeadsetChanged", DBUS_TYPE_STRING, ¶m, DBUS_TYPE_INVALID); + + dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DefaultDeviceChanged", + DBUS_TYPE_STRING, ¶m, + DBUS_TYPE_INVALID); + + device_store(default_dev, TRUE); } dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, -- cgit From f0305ee7aad91c359ecc2e1bc1e92eb6b4fb0fa9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 18:28:06 +0000 Subject: Update API description --- audio/audio-api.txt | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index d5f587c4..7e06b1f6 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -9,6 +9,10 @@ Copyright (C) 2005-2006 Brad Midgley org.bluez.audio.Manager interface ================================= +This interface is for managing remote audio devices. It provides methods for +creating and removing D-Bus objects representing remote audio devices. These +objects implement one or more of the other interfaces listed in this document. + Object path /org/bluez/audio Methods @@ -94,6 +98,8 @@ Signals org.bluez.audio.Device interface ================================ +This interface is implemented by all remote device objects. + Object path(s) /org/bluez/audio/device* Methods string GetAddress() [experimental] @@ -109,14 +115,25 @@ Methods string GetAddress() [experimental] org.bluez.audio.Headset interface ================================= +This interface provides access to headsets that implement the HSP and/or HFP +profiles. + Object path(s) /org/bluez/audio/device* Methods void Connect() + Connect to the HSP/HFP service on the remote device. + void Disconnect() + Disconnect from the HSP/HFP service on the remote + device. + boolean IsConnected() + Returns TRUE if there is a active connection to the + HSP/HFP connection on the remote device. + void IndicateCall() Indicate an incoming call on the headset @@ -187,8 +204,13 @@ Signals void AnswerRequested() The microphone gain changed. -org.bluez.audio.Source interface -================================ +org.bluez.audio.Gateway interface +================================= + +[not yet implemented] + +This interface is available for remote devices which can function in the Audio +Gateway role of the HSP and/or HFP profiles. Object path(s) /org/bluez/audio/device* @@ -196,4 +218,61 @@ Object path(s) /org/bluez/audio/device* org.bluez.audio.Sink interface ============================== +This interface is available for remote devices which contain a A2DP Sink. + +Object path(s) /org/bluez/audio/device* + +Methods void Connect() + + Connect and setup a stream to a A2DP sink on the + remote device. + + void Disconnect() + + Disconnect from the remote device. + + boolean IsConnected() + + Returns TRUE if a stream is setup to a A2DP sink on + the remote device. + +Signals void Connected() + + Sent when a successful connection has been made to the + remote A2DP Sink + + void Disconnected() + + Sent when the device has been disconnected from. + + +org.bluez.audio.Source interface +================================ + +[not yet implemented] + +This interface is available for remote devices which implement a A2DP source. + +Object path(s) /org/bluez/audio/device* + + +org.bluez.audio.Control interface +================================= + +[not yet implemented] + +This interface is available for remote devices which implement support for a +AVRCP controller. + +Object path(s) /org/bluez/audio/device* + + +org.bluez.audio.Target interface +================================ + +[not yet implemented] + +This interface is available for remote devices which implement support for a +AVRCP target. + Object path(s) /org/bluez/audio/device* -- cgit From 5b5cfaadb312600c58362617080e29fbf036712c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 19:14:26 +0000 Subject: Implement support for incoming Start command --- audio/a2dp.c | 6 ++++- audio/avdtp.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index fd355302..7574dbbb 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -188,7 +188,11 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Sink: Start_Ind"); else debug("SBC Source: Start_Ind"); - return TRUE; + + /* Refuse to go into streaming state since this action should only be + * initiated by alsa */ + *err = AVDTP_NOT_SUPPORTED_COMMAND; + return FALSE; } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, diff --git a/audio/avdtp.c b/audio/avdtp.c index febef897..01571996 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -91,14 +91,19 @@ struct avdtp_header { uint8_t rfa0:2; } __attribute__ ((packed)); -typedef struct seid_info { +struct seid_info { uint8_t rfa0:1; uint8_t inuse:1; uint8_t seid:6; uint8_t rfa2:3; uint8_t type:1; uint8_t media_type:4; -} __attribute__ ((packed)) seid_info_t; +} __attribute__ ((packed)); + +struct seid { + uint8_t rfa0:2; + uint8_t seid:6; +} __attribute__ ((packed)); /* packets */ @@ -120,6 +125,12 @@ struct getcap_resp { uint8_t caps[0]; } __attribute__ ((packed)); +struct start_req { + struct avdtp_header header; + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + struct seid_req { struct avdtp_header header; uint8_t rfa0:2; @@ -150,9 +161,9 @@ struct conf_rej { struct stream_rej { struct avdtp_header header; - uint8_t rfa0; + uint8_t rfa0:2; uint8_t acp_seid:6; - uint8_t error_code; + uint8_t error; } __attribute__ ((packed)); struct reconf_req { @@ -889,10 +900,60 @@ failed: return avdtp_send(session, &rej, sizeof(rej)); } -static gboolean avdtp_start_cmd(struct avdtp *session, struct seid_req *req, +static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct gen_resp *rsp = (struct gen_resp *) session->buf; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct start_req)) { + error("Too short start request"); + return FALSE; + } + + seid_count = 1 + (sizeof(struct start_req) - size); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + if (sep->state != AVDTP_STATE_OPEN) { + err = AVDTP_BAD_STATE; + goto failed; + } + + if (sep->ind && sep->ind->start) { + if (!sep->ind->start(session, sep, stream, &err)) + goto failed; + } + + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + } + + init_response(&rsp->header, &req->header, TRUE); + + return avdtp_send(session, rsp, sizeof(struct gen_resp)); + +failed: + memset(&rej, 0, sizeof(rej)); + init_response(&rej.header, &req->header, FALSE); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, @@ -905,7 +966,7 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, uint8_t err; if (size < sizeof(struct seid_req)) { - error("Too short abort request"); + error("Too short close request"); return FALSE; } @@ -1706,7 +1767,7 @@ static gboolean stream_rej_to_err(struct stream_rej *rej, int size, return FALSE; } - avdtp_error_init(err, AVDTP_ERROR_ERROR_CODE, rej->error_code); + avdtp_error_init(err, AVDTP_ERROR_ERROR_CODE, rej->error); if (acp_seid) *acp_seid = rej->acp_seid; @@ -2074,7 +2135,7 @@ int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) { - struct seid_req req; + struct start_req req; if (!g_slist_find(session->streams, stream)) return -EINVAL; @@ -2084,7 +2145,7 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_START); - req.acp_seid = stream->rseid; + req.first_seid.seid = stream->rseid; return send_request(session, FALSE, stream, &req, sizeof(req)); } -- cgit From bc37b7850f1bd8018d3b42d6530bf6792b715613 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 19:52:01 +0000 Subject: Add some error checks to sink_get_config --- audio/sink.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 155170e6..2219a9e4 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -362,9 +362,12 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int pkt_len, struct ipc_data_cfg **rsp, int *fd) { struct sink *sink = dev->sink; - int err = EINVAL; + int err; struct pending_connect *c = NULL; + if (sink->c) + return -EBUSY; + if (sink->state == AVDTP_STATE_STREAMING) goto proceed; @@ -375,27 +378,30 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, c->sock = sock; c->pkt = g_malloc(pkt_len); memcpy(c->pkt, req, pkt_len); - sink->c = c; if (sink->state == AVDTP_STATE_IDLE) err = avdtp_discover(sink->session, discovery_complete, dev); else if (sink->state < AVDTP_STATE_STREAMING) err = avdtp_start(sink->session, sink->stream); else - goto error; + err = -EINVAL; if (err < 0) - goto error; + goto failed; + + sink->c = c; return 1; proceed: - if (!a2dp_get_config(sink->stream, rsp, fd)) - goto error; + if (!a2dp_get_config(sink->stream, rsp, fd)) { + err = -EINVAL; + goto failed; + } return 0; -error: +failed: if (c) pending_connect_free(c); return -err; -- cgit From f1a26f036b1fd1aa8f31dfbe2d857b0bb31abaa1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 19:59:52 +0000 Subject: Handle unix client disconnect properly before we have received a configuration message --- audio/unix.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index d977633c..52b27809 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -162,7 +162,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) if (cond & (G_IO_HUP | G_IO_ERR)) { debug("Unix client disconnected"); - device_set_state(client->dev, STATE_CONNECTED); + if (client->dev) + device_set_state(client->dev, STATE_CONNECTED); goto failed; } @@ -232,7 +233,7 @@ static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) debug("Accepted new client connection on unix socket"); - client = g_new(struct unix_client, 1); + client = g_new0(struct unix_client, 1); client->sock = cli_sk; clients = g_slist_append(clients, client); -- cgit From a87207e7f3b81bf3bbea9cd4b92de9b274cca3ac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 20:07:18 +0000 Subject: Add extra error log to sink_get_config --- audio/sink.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 2219a9e4..237fcaee 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -365,8 +365,10 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int err; struct pending_connect *c = NULL; - if (sink->c) + if (sink->c) { + error("sink_get_config: another request already in progress"); return -EBUSY; + } if (sink->state == AVDTP_STATE_STREAMING) goto proceed; -- cgit From ab81dc1b10048188c2005dc29bbdb45f42c10c95 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 20:33:35 +0000 Subject: Don't perform any additional actions if not the initiator of a stream --- audio/sink.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 237fcaee..64d0a27d 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -113,6 +113,10 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); + + if (!sink->initiator) + break; + if (sink->c && sink->c->pkt) { cmd_err = avdtp_start(sink->session, stream); if (cmd_err < 0) { -- cgit From ca36a9c81adc2d7c0b0d8ddaa3e0e582991c1ca3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 21:16:55 +0000 Subject: Fix error checking order in sink_get_config --- audio/sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 64d0a27d..0020a7ce 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -369,14 +369,14 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int err; struct pending_connect *c = NULL; + if (sink->state == AVDTP_STATE_STREAMING) + goto proceed; + if (sink->c) { error("sink_get_config: another request already in progress"); return -EBUSY; } - if (sink->state == AVDTP_STATE_STREAMING) - goto proceed; - if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); -- cgit From fe09651ef69034b75126e0678455671ce57646fa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 13 Aug 2007 21:43:30 +0000 Subject: Rename pending_connect struct to something more accurate to its usage --- audio/sink.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 0020a7ce..4bf17e01 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -45,7 +45,7 @@ #include "unix.h" #include "sink.h" -struct pending_connect { +struct pending_request { DBusMessage *msg; struct ipc_packet *pkt; int pkt_len; @@ -56,12 +56,12 @@ struct sink { struct avdtp *session; struct avdtp_stream *stream; uint8_t state; - struct pending_connect *c; + struct pending_request *c; DBusConnection *conn; gboolean initiator; }; -static void pending_connect_free(struct pending_connect *c) +static void pending_connect_free(struct pending_request *c) { if (c->pkt) g_free(c->pkt); @@ -76,7 +76,7 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, { struct device *dev = user_data; struct sink *sink = dev->sink; - struct pending_connect *c = NULL; + struct pending_request *c = NULL; DBusMessage *reply; int cmd_err; @@ -243,7 +243,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, { struct device *dev = data; struct sink *sink = dev->sink; - struct pending_connect *c; + struct pending_request *c; int err; if (!sink->session) @@ -255,7 +255,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, if (sink->state >= AVDTP_STATE_OPEN) return err_already_connected(conn, msg); - c = g_new0(struct pending_connect, 1); + c = g_new0(struct pending_request, 1); c->msg = dbus_message_ref(msg); sink->c = c; @@ -276,7 +276,7 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, { struct device *device = data; struct sink *sink = device->sink; - struct pending_connect *c; + struct pending_request *c; int err; if (!sink->session) @@ -294,7 +294,7 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, return err_failed(conn, msg, strerror(-err)); } - c = g_new0(struct pending_connect, 1); + c = g_new0(struct pending_request, 1); c->msg = dbus_message_ref(msg); sink->c = c; @@ -367,7 +367,7 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, { struct sink *sink = dev->sink; int err; - struct pending_connect *c = NULL; + struct pending_request *c = NULL; if (sink->state == AVDTP_STATE_STREAMING) goto proceed; @@ -380,7 +380,7 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); - c = g_new0(struct pending_connect, 1); + c = g_new0(struct pending_request, 1); c->sock = sock; c->pkt = g_malloc(pkt_len); memcpy(c->pkt, req, pkt_len); @@ -462,6 +462,7 @@ void sink_set_state(struct device *dev, avdtp_state_t state) default: goto failed; } + failed: error("%s: Error changing states", dev->path); } -- cgit From 6e2276373f7f80dfd5947371fc46a35b3f8c48b9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Aug 2007 11:03:24 +0000 Subject: Also call stream state callback in error situations --- audio/avdtp.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 01571996..7efed380 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -463,18 +463,24 @@ static void avdtp_sep_set_state(struct avdtp *session, { struct avdtp_stream *stream = sep->stream; avdtp_state_t old_state; + struct avdtp_error err, *err_ptr = NULL; - if (sep->state == state) - return; - - debug("stream state changed: %s -> %s", avdtp_statestr(sep->state), - avdtp_statestr(state)); + if (sep->state == state) { + avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO); + debug("stream state change failed: %s", avdtp_strerror(&err)); + err_ptr = &err; + } else { + err_ptr = NULL; + debug("stream state changed: %s -> %s", + avdtp_statestr(sep->state), + avdtp_statestr(state)); + } old_state = sep->state; sep->state = state; if (stream && stream->cb) - stream->cb(stream, old_state, state, NULL, + stream->cb(stream, old_state, state, err_ptr, stream->user_data); if (state == AVDTP_STATE_IDLE) { -- cgit From afc2f9e31b18b2572e90902c86d6b75368e1e9c6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 14 Aug 2007 16:49:05 +0000 Subject: Fix use of bluetooth_hw_params for a2dp device. --- audio/pcm_bluetooth.c | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 62d77fe5..d0601a9f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -184,7 +184,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) return 0; } -static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) +static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, + snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; uint32_t period_count = io->buffer_size / io->period_size; @@ -213,6 +214,30 @@ static int bluetooth_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params return -err; } +static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, + snd_pcm_hw_params_t *params) +{ + struct bluetooth_data *data = io->private_data; + uint32_t period_count = io->buffer_size / io->period_size; + int opt_name, err; + struct timeval t = { 0, period_count }; + + DBG("fd = %d, period_count = %d", data->stream_fd, period_count); + + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SO_SNDTIMEO : SO_RCVTIMEO; + + if (setsockopt(data->stream_fd, SOL_SOCKET, opt_name, &t, + sizeof(t)) == 0) + return 0; + + err = errno; + SNDERR("%s (%d)", strerror(err), err); + bluetooth_close(io); + + return -err; +} + static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, @@ -364,7 +389,7 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - while (count++ < 50) { + while (count++ < 10) { #ifdef ENABLE_DEBUG gettimeofday(&send_date, NULL); #endif @@ -389,18 +414,17 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) timersub(&send_date, &prev_date, &send_delay); timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); - DBG("send %d (cumul=%d) samples (delay=%ld ms," + printf("send %d (cumul=%d) samples (delay=%ld ms," " real=%ld ms, theo=%ld ms," - " delta=%ld ms).", a2dp->samples, + " delta=%ld ms).\n", a2dp->samples, a2dp->nsamples, delay, real, theo, delta); } #endif - if (written >= 0) + if (written == a2dp->count) break; - if (errno != EAGAIN) - break; + a2dp->count -= written; DBG("send (retry)."); usleep(150000); @@ -412,8 +436,8 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) /* Send our data */ if (written != a2dp->count) - DBG("Wrote %d not %d bytes", written, a2dp->count); -#if 0 + printf("Wrote %d not %d bytes\n", written, a2dp->count); +#ifdef ENABLE_DEBUG else { /* Measure bandwith usage */ struct timeval now = { 0, 0 }; @@ -426,8 +450,8 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) gettimeofday(&now, NULL); timersub(&now, &a2dp->bandwithtimestamp, &interval); if(interval.tv_sec > 0) - DBG("Bandwith: %d (%d kbps)", a2dp->bandwithcount, - a2dp->bandwithcount/128); + printf("Bandwith: %d (%d kbps)\n", a2dp->bandwithcount, + a2dp->bandwithcount / 128); a2dp->bandwithtimestamp = now; a2dp->bandwithcount = 0; } @@ -531,7 +555,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hw_params, + .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, }; @@ -541,7 +565,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hw_params, + .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_read, }; @@ -551,7 +575,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hw_params, + .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_write, }; @@ -561,7 +585,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hw_params, + .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_read, }; -- cgit From 98a002b710efe17ec1c65cb6e338908361549a72 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Aug 2007 17:25:35 +0000 Subject: Cleanup the sbc codec when the plugin is deinitialized --- audio/pcm_bluetooth.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d0601a9f..6eb59a7d 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -165,6 +165,15 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) return 0; } +static int bluetooth_a2dp_close(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + + sbc_finish(&data->a2dp.sbc); + + return bluetooth_close(io); +} + static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -574,7 +583,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, - .close = bluetooth_close, + .close = bluetooth_a2dp_close, .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_write, @@ -584,7 +593,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, - .close = bluetooth_close, + .close = bluetooth_a2dp_close, .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_read, -- cgit From 331b4149a00410541af376a9ffce540bdada06bd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Aug 2007 19:59:09 +0000 Subject: Don't call a alsa callback from inside a alsa callback --- audio/pcm_bluetooth.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6eb59a7d..1734163a 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -218,7 +218,6 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, err = errno; SNDERR("%s (%d)", strerror(err), err); - bluetooth_close(io); return -err; } -- cgit From 6a069310ac7270e67c58dd13674c4a13860356a0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Aug 2007 19:59:40 +0000 Subject: Reset sink->c before calling get_config to avoid EBUSY --- audio/sink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 4bf17e01..f7052fb6 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -140,6 +140,8 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, sink->state = new_state; if (c) { + sink->c = NULL; + if (c->msg) { reply = dbus_message_new_method_return(c->msg); send_message_and_unref(dev->conn, reply); @@ -159,7 +161,6 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, } pending_connect_free(c); - sink->c = NULL; } return; -- cgit From d661010f0da37d072e9f3e0adcc94964fea6a054 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 14 Aug 2007 22:47:33 +0000 Subject: Minor fixes to alsa plugin. --- audio/pcm_bluetooth.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 1734163a..e7fa6878 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -42,7 +42,7 @@ //#define ENABLE_DEBUG -#define BUFFER_SIZE 1024 +#define BUFFER_SIZE 2048 #ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) @@ -151,6 +151,9 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->stream_fd >= 0) close(data->stream_fd); + if (data->cfg.codec == CFG_CODEC_SBC) + sbc_finish(&data->a2dp.sbc); + free(data); } @@ -241,7 +244,6 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, err = errno; SNDERR("%s (%d)", strerror(err), err); - bluetooth_close(io); return -err; } @@ -548,7 +550,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, a2dp->samples += encoded / frame_size; a2dp->nsamples += encoded / frame_size; /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + codesize / frame_size) + data->hw_ptr = (data->hw_ptr + encoded / frame_size) % io->buffer_size; ret = frames_to_read; -- cgit From b45b19f412b9f8d18181ff450bfce2f0f21fb561 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 08:38:32 +0000 Subject: A2DP specific cleanup callback is unnecessary after the previous fix to bluetooth_exit --- audio/pcm_bluetooth.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index e7fa6878..7977c1a6 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -168,15 +168,6 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) return 0; } -static int bluetooth_a2dp_close(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - - sbc_finish(&data->a2dp.sbc); - - return bluetooth_close(io); -} - static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -584,7 +575,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, - .close = bluetooth_a2dp_close, + .close = bluetooth_close, .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_write, @@ -594,7 +585,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { .start = bluetooth_start, .stop = bluetooth_stop, .pointer = bluetooth_pointer, - .close = bluetooth_a2dp_close, + .close = bluetooth_close, .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_read, -- cgit From 1d6c756e6d931b102b98fa7b2dbbebab4c8960e6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 08:45:06 +0000 Subject: STREAMING->OPEN happens first when Suspend response is received --- audio/avdtp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 7efed380..d313899d 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2181,7 +2181,6 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream) int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) { struct seid_req req; - int ret; if (!g_slist_find(session->streams, stream)) return -EINVAL; @@ -2193,12 +2192,7 @@ int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) init_request(&req.header, AVDTP_SUSPEND); req.acp_seid = stream->rseid; - ret = send_request(session, FALSE, stream, &req, sizeof(req)); - if (ret == 0) - avdtp_sep_set_state(session, stream->lsep, - AVDTP_STATE_OPEN); - - return ret; + return send_request(session, FALSE, stream, &req, sizeof(req)); } int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) -- cgit From 1c28d6bfc5d9f01956dfbbca1c04df10cf68591f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:05:36 +0000 Subject: Fix CONFIGURED->OPEN state change --- audio/avdtp.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index d313899d..a13abd23 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1146,8 +1146,6 @@ static void handle_transport_connect(struct avdtp *session, int sock, stream->io = g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) transport_cb, stream); g_io_channel_unref(channel); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); } static void init_request(struct avdtp_header *header, int request_id) -- cgit From 49d7d366bbbe13d0ebdb79da908aee5ca5429dcd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:17:03 +0000 Subject: Switch to OPEN state when receiving Open accept response --- audio/avdtp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index a13abd23..9da8730f 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1608,11 +1608,15 @@ static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stre { struct avdtp_local_sep *sep = stream->lsep; - if (l2cap_connect(session) < 0) + if (l2cap_connect(session) < 0) { avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); + return FALSE; + } session->pending_open = stream; + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + return TRUE; } -- cgit From 39a89d37d02285196b667079ad851e01b3575fe7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:25:34 +0000 Subject: avdtp_send returns a boolean and not a int --- audio/avdtp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 9da8730f..db5f4ef4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1473,9 +1473,10 @@ static int send_req(struct avdtp *session, gboolean priority, /* FIXME: Should we retry to send if the buffer was not totally sent or in case of EINTR? */ - err = avdtp_send(session, req->msg, req->msg_size); - if (err < 0) + if (!avdtp_send(session, req->msg, req->msg_size)) { + err = -EIO; goto failed; + } session->req = req; -- cgit From c6b43a62ce437203e710bf87a2e00214e97d4b23 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:28:02 +0000 Subject: send_request returns int and not a boolean --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index db5f4ef4..f35ff5ff 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1440,7 +1440,7 @@ static gboolean request_timeout(gpointer user_data) init_request(&sreq.header, AVDTP_ABORT); sreq.acp_seid = seid; - if (!send_request(session, TRUE, stream, &sreq, sizeof(sreq))) { + if (send_request(session, TRUE, stream, &sreq, sizeof(sreq)) < 0) { error("Unable to send abort request"); goto failed; } -- cgit From 962e84af6703db98d6fccaa654502f040619a846 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:56:28 +0000 Subject: Use FindAdapter to resolve adapter paths --- audio/device.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 450b2afe..87ddf5b7 100644 --- a/audio/device.c +++ b/audio/device.c @@ -135,12 +135,67 @@ static void device_unregister(DBusConnection *conn, void *data) device_free(device); } +char *find_adapter(DBusConnection *conn, bdaddr_t *src) +{ + DBusMessage *msg, *reply; + DBusError derr; + char address[18], *addr_ptr = address; + char *path, *ret; + + msg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Manager", + "FindAdapter"); + if (!msg) { + error("Unable to allocate new method call"); + return NULL; + } + + ba2str(src, address); + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, + &derr); + + dbus_message_unref(msg); + + if (dbus_error_is_set(&derr) || + dbus_set_error_from_message(&derr, reply)) { + error("FindAdapter(%s) failed: %s", address, derr.message); + dbus_error_free(&derr); + return NULL; + } + + dbus_error_init(&derr); + dbus_message_get_args(reply, &derr, + DBUS_TYPE_STRING, &path, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error("Unable to get message args"); + dbus_message_unref(reply); + dbus_error_free(&derr); + return FALSE; + } + + ret = g_strdup(path); + + dbus_message_unref(reply); + + debug("Got path %s for adapter with address %s", ret, address); + + return ret; +} + struct device *device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) { struct device *dev; bdaddr_t src; int dev_id; + char *adapter_path; if (!conn || !path) return NULL; @@ -150,6 +205,8 @@ struct device *device_register(DBusConnection *conn, if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return NULL; + adapter_path = find_adapter(conn, &src); + dev = g_new0(struct device, 1); if (!dbus_connection_create_object_path(conn, path, dev, @@ -171,8 +228,7 @@ struct device *device_register(DBusConnection *conn, bacpy(&dev->dst, bda); bacpy(&dev->src, &src); dev->conn = dbus_connection_ref(conn); - dev->adapter_path = g_malloc0(16); - snprintf(dev->adapter_path, 16, "/org/bluez/hci%d", dev_id); + dev->adapter_path = adapter_path; return dev; } -- cgit From a79bcc5733cb5898abb878fb3cfe8ba27fef7bdf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 09:58:19 +0000 Subject: check find_adapter return value for NULL --- audio/device.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 87ddf5b7..63ab7778 100644 --- a/audio/device.c +++ b/audio/device.c @@ -206,6 +206,8 @@ struct device *device_register(DBusConnection *conn, return NULL; adapter_path = find_adapter(conn, &src); + if (!adapter_path) + return NULL; dev = g_new0(struct device, 1); -- cgit From b1cd269c68b76018c2aabd550d37b3b7348e5cd6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 10:00:51 +0000 Subject: Yet another fix (memory leak) to using find_adapter --- audio/device.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 63ab7778..fe3af8cb 100644 --- a/audio/device.c +++ b/audio/device.c @@ -195,7 +195,6 @@ struct device *device_register(DBusConnection *conn, struct device *dev; bdaddr_t src; int dev_id; - char *adapter_path; if (!conn || !path) return NULL; @@ -205,12 +204,14 @@ struct device *device_register(DBusConnection *conn, if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return NULL; - adapter_path = find_adapter(conn, &src); - if (!adapter_path) - return NULL; - dev = g_new0(struct device, 1); + dev->adapter_path = find_adapter(conn, &src); + if (!dev->adapter_path) { + device_free(dev); + return NULL; + } + if (!dbus_connection_create_object_path(conn, path, dev, device_unregister)) { error("D-Bus failed to register %s path", path); @@ -230,7 +231,6 @@ struct device *device_register(DBusConnection *conn, bacpy(&dev->dst, bda); bacpy(&dev->src, &src); dev->conn = dbus_connection_ref(conn); - dev->adapter_path = adapter_path; return dev; } -- cgit From f802c1c270aadd8e6c019cce1ee6a89f5c784a65 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 10:36:16 +0000 Subject: Improve avdtp_write error handling --- audio/pcm_bluetooth.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 7977c1a6..c75f8af3 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -366,8 +366,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) { - int count = 0; - int written; + int count = 0, written = 0, ret = 0; struct rtp_header *header; struct rtp_payload *payload; struct bluetooth_a2dp *a2dp = &data->a2dp; @@ -394,8 +393,18 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) #ifdef ENABLE_DEBUG gettimeofday(&send_date, NULL); #endif - written = send(data->stream_fd, a2dp->buffer, a2dp->count, + ret = send(data->stream_fd, a2dp->buffer, a2dp->count, nonblock ? MSG_DONTWAIT : 0); + if (ret < 0) { + ret = -errno; + if (errno == EAGAIN) + goto retry; + fprintf(stderr, "send: %s (%d)\n", strerror(errno), + errno); + goto done; + } + + written += ret; #ifdef ENABLE_DEBUG if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { @@ -427,6 +436,7 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) a2dp->count -= written; +retry: DBG("send (retry)."); usleep(150000); } @@ -435,9 +445,9 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) prev_date = send_date; #endif - /* Send our data */ if (written != a2dp->count) printf("Wrote %d not %d bytes\n", written, a2dp->count); + #ifdef ENABLE_DEBUG else { /* Measure bandwith usage */ @@ -460,13 +470,18 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) a2dp->bandwithcount += written; #endif + +done: /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; a2dp->samples = 0; a2dp->seq_num++; - return written; + if (written > 0) + return written; + + return ret; } static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, @@ -532,8 +547,14 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); - if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) - avdtp_write(data, io->nonblock); + if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { + ret = avdtp_write(data, io->nonblock); + if (ret < 0) { + if (-ret == EPIPE) + ret = -EIO; + goto done; + } + } memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); a2dp->count += a2dp->sbc.len; -- cgit From c5c555e1b78f4301abe1e9ee46d65eb8f215ee53 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 11:34:56 +0000 Subject: Fix resuming of stream on rapid unix client reconnects --- audio/sink.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index f7052fb6..bdbabab2 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -59,6 +59,8 @@ struct sink { struct pending_request *c; DBusConnection *conn; gboolean initiator; + gboolean suspending; + gboolean resume; }; static void pending_connect_free(struct pending_request *c) @@ -108,6 +110,8 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, } break; case AVDTP_STATE_OPEN: + sink->suspending = FALSE; + if (old_state == AVDTP_STATE_CONFIGURED) dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, @@ -117,13 +121,14 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, if (!sink->initiator) break; - if (sink->c && sink->c->pkt) { + if (sink->resume || (sink->c && sink->c->pkt)) { cmd_err = avdtp_start(sink->session, stream); if (cmd_err < 0) { error("Error on avdtp_start %s (%d)", strerror(-cmd_err), cmd_err); goto failed; } + sink->resume = FALSE; } else c = sink->c; @@ -370,7 +375,7 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, int err; struct pending_request *c = NULL; - if (sink->state == AVDTP_STATE_STREAMING) + if (!sink->suspending && sink->state == AVDTP_STATE_STREAMING) goto proceed; if (sink->c) { @@ -390,7 +395,10 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, err = avdtp_discover(sink->session, discovery_complete, dev); else if (sink->state < AVDTP_STATE_STREAMING) err = avdtp_start(sink->session, sink->stream); - else + else if (sink->suspending) { + sink->resume = TRUE; + err = 0; + } else err = -EINVAL; if (err < 0) @@ -451,8 +459,10 @@ void sink_set_state(struct device *dev, avdtp_state_t state) case AVDTP_STATE_STREAMING: if (state == AVDTP_STATE_OPEN) { err = avdtp_suspend(sink->session, sink->stream); - if (err == 0) + if (err == 0) { + sink->suspending = TRUE; return; + } } else if (state == AVDTP_STATE_IDLE) { err = avdtp_close(sink->session, sink->stream); -- cgit From 47c2c74c8a11fcb3d53e4e124c317d243b5a12c0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 11:42:23 +0000 Subject: The resume variable is redundant --- audio/sink.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index bdbabab2..50d45718 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -60,7 +60,6 @@ struct sink { DBusConnection *conn; gboolean initiator; gboolean suspending; - gboolean resume; }; static void pending_connect_free(struct pending_request *c) @@ -121,14 +120,13 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, if (!sink->initiator) break; - if (sink->resume || (sink->c && sink->c->pkt)) { + if (sink->c && sink->c->pkt) { cmd_err = avdtp_start(sink->session, stream); if (cmd_err < 0) { error("Error on avdtp_start %s (%d)", strerror(-cmd_err), cmd_err); goto failed; } - sink->resume = FALSE; } else c = sink->c; @@ -395,10 +393,9 @@ int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, err = avdtp_discover(sink->session, discovery_complete, dev); else if (sink->state < AVDTP_STATE_STREAMING) err = avdtp_start(sink->session, sink->stream); - else if (sink->suspending) { - sink->resume = TRUE; + else if (sink->suspending) err = 0; - } else + else err = -EINVAL; if (err < 0) -- cgit From 931bfd25641a14b82c6c82db20eeb97bc57fafe6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 14:13:27 +0000 Subject: Implement support for incoming suspend commands --- audio/avdtp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index f35ff5ff..bc9081a4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -131,6 +131,12 @@ struct start_req { struct seid other_seids[0]; } __attribute__ ((packed)); +struct suspend_req { + struct avdtp_header header; + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + struct seid_req { struct avdtp_header header; uint8_t rfa0:2; @@ -1013,10 +1019,60 @@ failed: return avdtp_send(session, &rej, sizeof(rej)); } -static gboolean avdtp_suspend_cmd(struct avdtp *session, struct seid_req *req, - int size) +static gboolean avdtp_suspend_cmd(struct avdtp *session, + struct suspend_req *req, int size) { - return avdtp_unknown_cmd(session, (void *) req, size); + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct gen_resp *rsp = (struct gen_resp *) session->buf; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct suspend_req)) { + error("Too short suspend request"); + return FALSE; + } + + seid_count = 1 + (sizeof(struct suspend_req) - size); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + if (sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + if (sep->ind && sep->ind->suspend) { + if (!sep->ind->suspend(session, sep, stream, &err)) + goto failed; + } + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + } + + init_response(&rsp->header, &req->header, TRUE); + + return avdtp_send(session, rsp, sizeof(struct gen_resp)); + +failed: + memset(&rej, 0, sizeof(rej)); + init_response(&rej.header, &req->header, FALSE); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, &rej, sizeof(rej)); } static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, -- cgit From 1addff77624b54445c9b81779ddc78892a126a45 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 22:57:49 +0000 Subject: call avdtp_start from open_cfm instead of state callback --- audio/a2dp.c | 23 +++++++++++++++++++++++ audio/a2dp.h | 3 +++ audio/sink.c | 14 +++++--------- 3 files changed, 31 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 7574dbbb..b8431bc6 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -50,6 +50,9 @@ static uint32_t source_record_id = 0; static struct avdtp_local_sep *sink_sep = NULL; static struct avdtp_local_sep *source_sep = NULL; +static struct avdtp *start_session = NULL; +static struct avdtp_stream *start_stream = NULL; + static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, @@ -175,10 +178,22 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { + int err; + if (sep == sink_sep) debug("SBC Sink: Open_Cfm"); else debug("SBC Source: Open_Cfm"); + + if (session != start_session || stream != start_stream) + return; + + start_session = NULL; + start_stream = NULL; + + err = avdtp_start(session, stream); + if (err < 0) + error("Error on avdtp_start %s (%d)", strerror(-err), err); } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -669,3 +684,11 @@ gboolean a2dp_get_config(struct avdtp_stream *stream, return TRUE; } + +void a2dp_start_stream_when_opened(struct avdtp *session, + struct avdtp_stream *stream) +{ + start_session = session; + start_stream = stream; + +} diff --git a/audio/a2dp.h b/audio/a2dp.h index b2653594..23416988 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -66,3 +66,6 @@ gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg, int *fd); + +void a2dp_start_stream_when_opened(struct avdtp *session, + struct avdtp_stream *stream); diff --git a/audio/sink.c b/audio/sink.c index 50d45718..fe8b6393 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -107,6 +107,9 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, cmd_err); goto failed; } + + if (sink->c && sink->c->pkt) + a2dp_start_stream_when_opened(sink->session, stream); break; case AVDTP_STATE_OPEN: sink->suspending = FALSE; @@ -120,16 +123,9 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, if (!sink->initiator) break; - if (sink->c && sink->c->pkt) { - cmd_err = avdtp_start(sink->session, stream); - if (cmd_err < 0) { - error("Error on avdtp_start %s (%d)", - strerror(-cmd_err), cmd_err); - goto failed; - } - } - else + if (!(sink->c && sink->c->pkt)) c = sink->c; + break; case AVDTP_STATE_STREAMING: c = sink->c; -- cgit From 5d2db7ef22c2fd0971f24f759b039f8a891447fa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 15 Aug 2007 23:22:58 +0000 Subject: According to spec. OPEN state is entered before calling suspend_cfm --- audio/avdtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index bc9081a4..eb00f8bb 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1712,11 +1712,11 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, { struct avdtp_local_sep *sep = stream->lsep; + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + if (sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream); - avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); - return TRUE; } -- cgit From cdd9e2e17ad674e5fc1a5ed19643880ef61d28c7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 15:42:10 +0000 Subject: Rework interfacing with the avdtp state machine --- audio/a2dp.c | 460 ++++++++++++++++++++++++++++++++++++++++++++++++-------- audio/a2dp.h | 16 +- audio/avdtp.c | 10 ++ audio/avdtp.h | 4 + audio/device.c | 25 +-- audio/device.h | 5 - audio/headset.c | 33 ++++ audio/headset.h | 5 + audio/sink.c | 325 +++++++++------------------------------ audio/sink.h | 3 - audio/unix.c | 215 ++++++++++++++++++++++++-- 11 files changed, 736 insertions(+), 365 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index b8431bc6..5302fb2d 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -42,16 +42,67 @@ #include "sink.h" #include "a2dp.h" +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +struct a2dp_sep { + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct device *used_by; + uint32_t record_id; + gboolean start_requested; + gboolean suspending; + gboolean starting; +}; + +struct a2dp_stream_cb { + a2dp_stream_cb_t cb; + void *user_data; + int id; +}; + +struct a2dp_stream_setup { + struct avdtp *session; + struct device *dev; + struct avdtp_stream *stream; + gboolean start; + gboolean canceled; + GSList *cb; +}; + static DBusConnection *connection = NULL; -static uint32_t sink_record_id = 0; -static uint32_t source_record_id = 0; +static struct a2dp_sep sink = { NULL, NULL, 0 }; +static struct a2dp_sep source = { NULL, NULL, 0 }; -static struct avdtp_local_sep *sink_sep = NULL; -static struct avdtp_local_sep *source_sep = NULL; +static struct a2dp_stream_setup *setup = NULL; -static struct avdtp *start_session = NULL; -static struct avdtp_stream *start_stream = NULL; +static void stream_setup_free(struct a2dp_stream_setup *s) +{ + if (s->session) + avdtp_unref(s->session); + g_slist_foreach(s->cb, (GFunc) g_free, NULL); + g_slist_free(s->cb); + g_free(s); + setup = NULL; +} + +static void setup_callback(struct a2dp_stream_cb *cb, + struct a2dp_stream_setup *s) +{ + cb->cb(s->session, s->dev, s->stream, cb->user_data); +} + +static void finalize_stream_setup(struct a2dp_stream_setup *s) +{ + g_slist_foreach(setup->cb, (GFunc) setup_callback, setup); + stream_setup_free(setup); +} static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -62,7 +113,7 @@ static gboolean setconf_ind(struct avdtp *session, struct device *dev; bdaddr_t addr; - if (sep == sink_sep) { + if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Ind"); return TRUE; } @@ -78,6 +129,8 @@ static gboolean setconf_ind(struct avdtp *session, return FALSE; } + source.stream = stream; + sink_new_stream(dev, session, stream); return TRUE; @@ -89,7 +142,7 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Get_Capability_Ind"); else debug("SBC Source: Get_Capability_Ind"); @@ -140,16 +193,33 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + int err; + + if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Cfm"); - else - debug("SBC Source: Set_Configuration_Cfm"); + return; + } + + debug("SBC Source: Set_Configuration_Cfm"); + + source.stream = stream; + + if (!setup) + return; + + err = avdtp_open(session, stream); + if (err < 0) { + error("Error on avdtp_open %s (%d)", strerror(-err), + -err); + setup->stream = FALSE; + finalize_stream_setup(setup); + } } static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Get_Configuration_Ind"); else debug("SBC Source: Get_Configuration_Ind"); @@ -159,7 +229,7 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Set_Configuration_Cfm"); else debug("SBC Source: Set_Configuration_Cfm"); @@ -168,7 +238,7 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Open_Ind"); else debug("SBC Source: Open_Ind"); @@ -178,28 +248,35 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - int err; - - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Open_Cfm"); else debug("SBC Source: Open_Cfm"); - if (session != start_session || stream != start_stream) + if (!setup) return; - start_session = NULL; - start_stream = NULL; + if (setup->canceled) { + avdtp_close(session, stream); + stream_setup_free(setup); + return; + } + + if (setup->start) { + if (avdtp_start(session, stream) == 0) + return; - err = avdtp_start(session, stream); - if (err < 0) - error("Error on avdtp_start %s (%d)", strerror(-err), err); + error("avdtp_start failed"); + setup->stream = NULL; + } + + finalize_stream_setup(setup); } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Start_Ind"); else debug("SBC Source: Start_Ind"); @@ -213,16 +290,27 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Start_Cfm"); else debug("SBC Source: Start_Cfm"); + + if (!setup) + return; + + if (setup->canceled) { + avdtp_close(session, stream); + stream_setup_free(setup); + return; + } + + finalize_stream_setup(setup); } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Suspend_Ind"); else debug("SBC Source: Suspend_Ind"); @@ -232,45 +320,68 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + if (sep == sink.sep) { debug("SBC Sink: Suspend_Cfm"); - else - debug("SBC Source: Suspend_Cfm"); + return; + } + + debug("SBC Source: Suspend_Cfm"); + + source.suspending = FALSE; + + if (source.start_requested) { + avdtp_start(session, stream); + source.start_requested = FALSE; + } } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) { debug("SBC Sink: Close_Ind"); - else - debug("SBC Source: Close_Ind"); + return TRUE; + } + + debug("SBC Source: Close_Ind"); + + source.stream = NULL; + return TRUE; } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + if (sep == sink.sep) { debug("SBC Sink: Close_Cfm"); - else - debug("SBC Source: Close_Cfm"); + return; + } + + debug("SBC Source: Close_Cfm"); + + source.stream = NULL; } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) { debug("SBC Sink: Abort_Ind"); - else - debug("SBC Source: Abort_Ind"); + return TRUE; + } + + debug("SBC Source: Abort_Ind"); + + source.stream = NULL; + return TRUE; } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: Abort_Cfm"); else debug("SBC Source: Abort_Cfm"); @@ -279,7 +390,7 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t *err) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: ReConfigure_Ind"); else debug("SBC Source: ReConfigure_Ind"); @@ -288,7 +399,7 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep) { - if (sep == sink_sep) + if (sep == sink.sep) debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); @@ -400,10 +511,10 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source avdtp_init(); if (enable_sink) { - source_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); - if (source_sep == NULL) + source.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, + AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm); + if (source.sep == NULL) return -1; if (a2dp_source_record(&buf) < 0) { @@ -411,19 +522,19 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source return -1; } - source_record_id = add_service_record(conn, &buf); + source.record_id = add_service_record(conn, &buf); free(buf.data); - if (!source_record_id) { + if (!source.record_id) { error("Unable to register A2DP Source service record"); return -1; } } if (enable_source) { - sink_sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, + sink.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, &ind, &cfm); - if (sink_sep == NULL) + if (sink.sep == NULL) return -1; if (a2dp_sink_record(&buf) < 0) { @@ -431,9 +542,9 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source return -1; } - sink_record_id = add_service_record(conn, &buf); + sink.record_id = add_service_record(conn, &buf); free(buf.data); - if (!sink_record_id) { + if (!sink.record_id) { error("Unable to register A2DP Sink service record"); return -1; } @@ -444,20 +555,24 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source void a2dp_exit() { - if (sink_sep) - avdtp_unregister_sep(sink_sep); + if (sink.sep) { + avdtp_unregister_sep(sink.sep); + sink.sep = NULL; + } - if (source_sep) - avdtp_unregister_sep(source_sep); + if (source.sep) { + avdtp_unregister_sep(source.sep); + source.sep = NULL; + } - if (source_record_id) { - remove_service_record(connection, source_record_id); - source_record_id = 0; + if (source.record_id) { + remove_service_record(connection, source.record_id); + source.record_id = 0; } - if (sink_record_id) { - remove_service_record(connection, sink_record_id); - sink_record_id = 0; + if (sink.record_id) { + remove_service_record(connection, sink.record_id); + sink.record_id = 0; } dbus_connection_unref(connection); @@ -561,7 +676,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, else if (supported->allocation_method & A2DP_ALLOCATION_SNR) cap->allocation_method = A2DP_ALLOCATION_SNR; - min_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + min_bitpool = MAX(default_bitpool(cap->frequency, cap->channel_mode), supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), supported->max_bitpool); @@ -685,10 +800,223 @@ gboolean a2dp_get_config(struct avdtp_stream *stream, return TRUE; } -void a2dp_start_stream_when_opened(struct avdtp *session, - struct avdtp_stream *stream) +static void discovery_complete(struct avdtp *session, GSList *seps, int err, + void *user_data) { - start_session = session; - start_stream = stream; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + GSList *caps = NULL; + + if (err < 0) { + error("Discovery failed: %s (%d)", strerror(-err), -err); + setup->stream = NULL; + finalize_stream_setup(setup); + return; + } + debug("Discovery complete"); + + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + A2DP_CODEC_SBC, &lsep, &rsep) < 0) { + error("No matching ACP and INT SEPs found"); + finalize_stream_setup(setup); + return; + } + + if (!a2dp_select_capabilities(rsep, &caps)) { + error("Unable to select remote SEP capabilities"); + finalize_stream_setup(setup); + return; + } + + err = avdtp_set_configuration(session, rsep, lsep, caps, + &setup->stream); + if (err < 0) { + error("avdtp_set_configuration: %s", strerror(-err)); + finalize_stream_setup(setup); + return; + } + + /* Notify sink.c of the new stream */ + sink_new_stream(setup->dev, session, setup->stream); +} + +gboolean a2dp_source_cancel_stream(int id) +{ + struct a2dp_stream_cb *cb_data; + GSList *l; + + if (!setup) + return FALSE; + + for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) { + struct a2dp_stream_cb *cb = l->data; + + if (cb->id == id) { + cb_data = cb; + break; + } + } + + if (!cb_data) + return FALSE; + + setup->cb = g_slist_remove(setup->cb, cb_data); + + if (!setup->cb) + setup->canceled = TRUE; + + return TRUE; +} + +int a2dp_source_request_stream(struct avdtp *session, struct device *dev, + gboolean start, a2dp_stream_cb_t cb, + void *user_data) +{ + struct a2dp_stream_cb *cb_data; + static unsigned int cb_id = 0; + + cb_data = g_new(struct a2dp_stream_cb, 1); + cb_data->cb = cb; + cb_data->user_data = user_data; + cb_data->id = cb_id++; + + if (setup) { + setup->canceled = FALSE; + setup->cb = g_slist_append(setup->cb, cb_data); + if (start) + setup->start = TRUE; + return cb_data->id; + } + + setup = g_new0(struct a2dp_stream_setup, 1); + setup->session = avdtp_ref(session); + setup->dev = dev; + setup->cb = g_slist_append(setup->cb, cb_data); + setup->start = start; + setup->stream = source.stream; + + switch (avdtp_sep_get_state(source.sep)) { + case AVDTP_STATE_IDLE: + if (avdtp_discover(session, discovery_complete, setup) < 0) + goto failed; + break; + case AVDTP_STATE_OPEN: + if (!start) { + g_idle_add((GSourceFunc) finalize_stream_setup, setup); + break; + } + if (source.starting) + break; + if (avdtp_start(session, source.stream) < 0) + goto failed; + break; + case AVDTP_STATE_STREAMING: + if (!start || !source.suspending) { + g_idle_add((GSourceFunc) finalize_stream_setup, setup); + return cb_data->id; + } + source.start_requested = TRUE; + break; + default: + goto failed; + } + + return cb_data->id; + +failed: + stream_setup_free(setup); + cb_id--; + return -1; +} + +gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) +{ + if (source.used_by) + return FALSE; + + source.used_by = dev; + + return TRUE; +} + +gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) +{ + avdtp_state_t state; + + if (!source.sep) + return FALSE; + + if (source.used_by != dev) + return FALSE; + + state = avdtp_sep_get_state(source.sep); + + source.used_by = NULL; + + if (!source.stream || state == AVDTP_STATE_IDLE) + return TRUE; + + switch (state) { + case AVDTP_STATE_OPEN: + /* Set timer here */ + break; + case AVDTP_STATE_STREAMING: + if (avdtp_suspend(session, source.stream) == 0) + source.suspending = TRUE; + break; + default: + break; + } + + return TRUE; +} + +gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) +{ + avdtp_state_t state; + + if (!source.sep) + return FALSE; + + if (source.used_by != dev) + return FALSE; + + state = avdtp_sep_get_state(source.sep); + + if (!source.stream || state != AVDTP_STATE_STREAMING) + return TRUE; + + if (avdtp_suspend(session, source.stream) == 0) { + source.suspending = TRUE; + return TRUE; + } + + return FALSE; +} + +gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) +{ + avdtp_state_t state; + + if (!source.sep) + return FALSE; + + if (source.used_by != dev) + return FALSE; + + state = avdtp_sep_get_state(source.sep); + + if (state < AVDTP_STATE_OPEN) { + error("a2dp_source_start_stream: no stream open"); + return FALSE; + } + + if (state == AVDTP_STATE_STREAMING) + return TRUE; + + if (avdtp_start(session, source.stream) < 0) + return FALSE; + + return TRUE; } diff --git a/audio/a2dp.h b/audio/a2dp.h index 23416988..3617afc4 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -58,6 +58,10 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev, + struct avdtp_stream *stream, + void *user_data); + int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source); void a2dp_exit(void); @@ -67,5 +71,13 @@ gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); gboolean a2dp_get_config(struct avdtp_stream *stream, struct ipc_data_cfg **cfg, int *fd); -void a2dp_start_stream_when_opened(struct avdtp *session, - struct avdtp_stream *stream); +int a2dp_source_request_stream(struct avdtp *session, struct device *dev, + gboolean start, a2dp_stream_cb_t cb, + void *user_data); +gboolean a2dp_source_cancel_stream(int id); + +gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); +gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session); +gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session); +gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session); + diff --git a/audio/avdtp.c b/audio/avdtp.c index eb00f8bb..7194177f 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1957,6 +1957,11 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) return avdtp_ref(session); } +gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst) +{ + return find_session(src, dst) == NULL ? FALSE : TRUE; +} + gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *mtu, GSList **caps) { @@ -2489,6 +2494,11 @@ const char *avdtp_strerror(struct avdtp_error *err) } } +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) +{ + return sep->state; +} + void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst) { if (src) diff --git a/audio/avdtp.h b/audio/avdtp.h index f7ca6308..0043c480 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -155,6 +155,8 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst); void avdtp_unref(struct avdtp *session); struct avdtp *avdtp_ref(struct avdtp *session); +gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst); + struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int size); @@ -196,6 +198,8 @@ int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media, int avdtp_unregister_sep(struct avdtp_local_sep *sep); +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); + const char *avdtp_strerror(struct avdtp_error *err); void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); diff --git a/audio/device.c b/audio/device.c index fe3af8cb..2a6498ee 100644 --- a/audio/device.c +++ b/audio/device.c @@ -328,21 +328,7 @@ void device_finish_sdp_transaction(struct device *dev) dbus_message_unref(reply); } -int device_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp, int *fd) -{ - if (dev->sink && sink_is_active(dev)) - return sink_get_config(dev, sock, req, pkt_len, rsp, fd); - else if (dev->headset && headset_is_active(dev)) - return headset_get_config(dev, sock, req, pkt_len, rsp, fd); - else if (dev->sink) - return sink_get_config(dev, sock, req, pkt_len, rsp, fd); - else if (dev->headset) - return headset_get_config(dev, sock, req, pkt_len, rsp, fd); - - return -EINVAL; -} - +#if 0 static avdtp_state_t ipc_to_avdtp_state(uint8_t ipc_state) { switch (ipc_state) { @@ -379,14 +365,7 @@ static headset_state_t ipc_to_hs_state(uint8_t ipc_state) return HEADSET_STATE_DISCONNECTED; } } - -void device_set_state(struct device *dev, uint8_t state) -{ - if (dev->sink && sink_is_active(dev)) - sink_set_state(dev, ipc_to_avdtp_state(state)); - else if (dev->headset && headset_is_active(dev)) - headset_set_state(dev, ipc_to_hs_state(state)); -} +#endif static uint8_t avdtp_to_ipc_state(avdtp_state_t state) { diff --git a/audio/device.h b/audio/device.h index d9a42ca1..a527c096 100644 --- a/audio/device.h +++ b/audio/device.h @@ -75,9 +75,4 @@ int device_remove_stored(struct device *dev); void device_finish_sdp_transaction(struct device *device); -int device_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp, int *fd); - -void device_set_state(struct device *dev, uint8_t state); - uint8_t device_get_state(struct device *dev); diff --git a/audio/headset.c b/audio/headset.c index b0e67ba2..056f9e18 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -100,6 +100,8 @@ struct headset { int sp_gain; int mic_gain; + + gboolean locked; }; static int rfcomm_connect(struct device *device, struct pending_connect *c); @@ -1570,3 +1572,34 @@ gboolean headset_is_active(struct device *dev) return FALSE; } + +gboolean headset_lock(struct device *dev, void *data) +{ + struct headset *hs = dev->headset; + + if (hs->locked) + return FALSE; + + hs->locked = TRUE; + + return TRUE; +} + +gboolean headset_unlock(struct device *dev, void *data) +{ + struct headset *hs = dev->headset; + + hs->locked = FALSE; + + return TRUE; +} + +gboolean headset_suspend(struct device *dev, void *data) +{ + return TRUE; +} + +gboolean headset_play(struct device *dev, void *data) +{ + return TRUE; +} diff --git a/audio/headset.h b/audio/headset.h index 2a65d136..29e2e496 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -68,3 +68,8 @@ void headset_set_state(struct device *dev, headset_state_t state); int headset_get_channel(struct device *dev); gboolean headset_is_active(struct device *dev); + +gboolean headset_lock(struct device *dev, void *data); +gboolean headset_unlock(struct device *dev, void *data); +gboolean headset_suspend(struct device *dev, void *data); +gboolean headset_play(struct device *dev, void *data); diff --git a/audio/sink.c b/audio/sink.c index fe8b6393..0f69bbf6 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -46,29 +46,29 @@ #include "sink.h" struct pending_request { + DBusConnection *conn; DBusMessage *msg; - struct ipc_packet *pkt; - int pkt_len; - int sock; + int id; }; struct sink { struct avdtp *session; struct avdtp_stream *stream; uint8_t state; - struct pending_request *c; + struct pending_request *connect; + struct pending_request *disconnect; DBusConnection *conn; gboolean initiator; gboolean suspending; }; -static void pending_connect_free(struct pending_request *c) +static void pending_request_free(struct pending_request *pending) { - if (c->pkt) - g_free(c->pkt); - if (c->msg) - dbus_message_unref(c->msg); - g_free(c); + if (pending->conn) + dbus_connection_unref(pending->conn); + if (pending->msg) + dbus_message_unref(pending->msg); + g_free(pending); } void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, @@ -77,12 +77,9 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, { struct device *dev = user_data; struct sink *sink = dev->sink; - struct pending_request *c = NULL; - DBusMessage *reply; - int cmd_err; if (err) - goto failed; + return; switch (new_state) { case AVDTP_STATE_IDLE: @@ -90,152 +87,64 @@ void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, AUDIO_SINK_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); + if (sink->disconnect) { + DBusMessage *reply; + struct pending_request *p; + + p = sink->disconnect; + sink->disconnect = NULL; + + reply = dbus_message_new_method_return(p->msg); + send_message_and_unref(p->conn, reply); + pending_request_free(p); + } + if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; } sink->stream = NULL; - c = sink->c; - break; - case AVDTP_STATE_CONFIGURED: - if (!sink->initiator) - break; - - cmd_err = avdtp_open(sink->session, stream); - if (cmd_err < 0) { - error("Error on avdtp_open %s (%d)", strerror(-cmd_err), - cmd_err); - goto failed; - } - - if (sink->c && sink->c->pkt) - a2dp_start_stream_when_opened(sink->session, stream); break; case AVDTP_STATE_OPEN: - sink->suspending = FALSE; - if (old_state == AVDTP_STATE_CONFIGURED) dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); - - if (!sink->initiator) - break; - - if (!(sink->c && sink->c->pkt)) - c = sink->c; - break; + case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_STREAMING: - c = sink->c; - break; case AVDTP_STATE_CLOSING: - break; case AVDTP_STATE_ABORTING: + default: break; } sink->state = new_state; - - if (c) { - sink->c = NULL; - - if (c->msg) { - reply = dbus_message_new_method_return(c->msg); - send_message_and_unref(dev->conn, reply); - } - if (c->pkt) { - struct ipc_data_cfg *rsp; - int ret, fd; - - ret = sink_get_config(dev, c->sock, c->pkt, - c->pkt_len, &rsp, &fd); - if (ret == 0) { - unix_send_cfg(c->sock, rsp, fd); - g_free(rsp); - } - else - unix_send_cfg(c->sock, NULL, -1); - } - - pending_connect_free(c); - } - - return; - -failed: - if (sink->c) { - if (sink->c->msg && err) - err_failed(dev->conn, sink->c->msg, - avdtp_strerror(err)); - - pending_connect_free(sink->c); - sink->c = NULL; - } - - if (new_state == AVDTP_STATE_IDLE) { - avdtp_unref(sink->session); - sink->session = NULL; - sink->stream = NULL; - } } -static void discovery_complete(struct avdtp *session, GSList *seps, int err, - void *user_data) +static void stream_setup_complete(struct avdtp *session, struct device *dev, + struct avdtp_stream *stream, + void *user_data) { - struct device *dev = user_data; struct sink *sink = dev->sink; - struct avdtp_local_sep *lsep; - struct avdtp_remote_sep *rsep; - GSList *caps = NULL; - const char *err_str = NULL; - - if (err < 0) { - error("Discovery failed"); - err_str = strerror(-err); - goto failed; - } - - debug("Discovery complete"); - - if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, - A2DP_CODEC_SBC, &lsep, &rsep) < 0) { - err_str = "No matching ACP and INT SEPs found"; - goto failed; - } + struct pending_request *pending; - if (!a2dp_select_capabilities(rsep, &caps)) { - err_str = "Unable to select remote SEP capabilities"; - goto failed; - } + pending = sink->connect; + sink->connect = NULL; - err = avdtp_set_configuration(session, rsep, lsep, caps, - &sink->stream); - if (err < 0) { - error("avdtp_set_configuration: %s", strerror(-err)); - err_str = "Unable to set configuration"; - goto failed; + if (stream) { + DBusMessage *reply; + reply = dbus_message_new_method_return(pending->msg); + send_message_and_unref(pending->conn, reply); } - - sink->initiator = TRUE; - - avdtp_stream_set_cb(session, sink->stream, stream_state_changed, dev); - - return; - -failed: - error("%s", err_str); - if (sink->c) { - if (sink->c->msg) - err_failed(dev->conn, sink->c->msg, err_str); - pending_connect_free(sink->c); - sink->c = NULL; - } - if (sink->session) { + else { + err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; } + + pending_request_free(pending); } static DBusHandlerResult sink_connect(DBusConnection *conn, @@ -243,31 +152,36 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, { struct device *dev = data; struct sink *sink = dev->sink; - struct pending_request *c; - int err; + struct pending_request *pending; + int id; if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); - if (sink->c) + if (sink->connect || sink->disconnect) return err_connect_failed(conn, msg, "Connect in progress"); if (sink->state >= AVDTP_STATE_OPEN) return err_already_connected(conn, msg); - c = g_new0(struct pending_request, 1); - c->msg = dbus_message_ref(msg); - sink->c = c; + pending = g_new0(struct pending_request, 1); + pending->conn = dbus_connection_ref(conn); + pending->msg = dbus_message_ref(msg); + sink->connect = pending; - err = avdtp_discover(sink->session, discovery_complete, data); - if (err < 0) { - pending_connect_free(c); - sink->c = NULL; + id = a2dp_source_request_stream(sink->session, dev, FALSE, + stream_setup_complete, pending); + if (id < 0) { + pending_request_free(pending); + sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; - return err_connect_failed(conn, msg, strerror(err)); + return err_connect_failed(conn, msg, + "Failed to request a stream"); } + pending->id = id; + return DBUS_HANDLER_RESULT_HANDLED; } @@ -276,27 +190,32 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, { struct device *device = data; struct sink *sink = device->sink; - struct pending_request *c; + struct pending_request *pending; int err; if (!sink->session) return err_not_connected(conn, msg); - if (sink->c) + if (sink->connect || sink->disconnect) return err_failed(conn, msg, strerror(EBUSY)); if (sink->state < AVDTP_STATE_OPEN) { + DBusMessage *reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; avdtp_unref(sink->session); sink->session = NULL; - } else { - err = avdtp_close(sink->session, sink->stream); - if (err < 0) - return err_failed(conn, msg, strerror(-err)); + return send_message_and_unref(conn, reply); } - c = g_new0(struct pending_request, 1); - c->msg = dbus_message_ref(msg); - sink->c = c; + err = avdtp_close(sink->session, sink->stream); + if (err < 0) + return err_failed(conn, msg, strerror(-err)); + + pending = g_new0(struct pending_request, 1); + pending->conn = dbus_connection_ref(conn); + pending->msg = dbus_message_ref(msg); + sink->disconnect = pending; return DBUS_HANDLER_RESULT_HANDLED; } @@ -355,66 +274,16 @@ void sink_free(struct device *dev) if (sink->session) avdtp_unref(sink->session); - if (sink->c) - pending_connect_free(sink->c); + if (sink->connect) + pending_request_free(sink->connect); + + if (sink->disconnect) + pending_request_free(sink->disconnect); g_free(sink); dev->sink = NULL; } -int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp, int *fd) -{ - struct sink *sink = dev->sink; - int err; - struct pending_request *c = NULL; - - if (!sink->suspending && sink->state == AVDTP_STATE_STREAMING) - goto proceed; - - if (sink->c) { - error("sink_get_config: another request already in progress"); - return -EBUSY; - } - - if (!sink->session) - sink->session = avdtp_get(&dev->src, &dev->dst); - - c = g_new0(struct pending_request, 1); - c->sock = sock; - c->pkt = g_malloc(pkt_len); - memcpy(c->pkt, req, pkt_len); - - if (sink->state == AVDTP_STATE_IDLE) - err = avdtp_discover(sink->session, discovery_complete, dev); - else if (sink->state < AVDTP_STATE_STREAMING) - err = avdtp_start(sink->session, sink->stream); - else if (sink->suspending) - err = 0; - else - err = -EINVAL; - - if (err < 0) - goto failed; - - sink->c = c; - - return 1; - -proceed: - if (!a2dp_get_config(sink->stream, rsp, fd)) { - err = -EINVAL; - goto failed; - } - - return 0; - -failed: - if (c) - pending_connect_free(c); - return -err; -} - gboolean sink_is_active(struct device *dev) { struct sink *sink = dev->sink; @@ -425,52 +294,6 @@ gboolean sink_is_active(struct device *dev) return FALSE; } -void sink_set_state(struct device *dev, avdtp_state_t state) -{ - struct sink *sink = dev->sink; - int err = 0; - - if (sink->state == state) - return; - - if (!sink->session || !sink->stream) - goto failed; - - switch (sink->state) { - case AVDTP_STATE_OPEN: - if (state == AVDTP_STATE_STREAMING) { - err = avdtp_start(sink->session, sink->stream); - if (err == 0) - return; - } - else if (state == AVDTP_STATE_IDLE) { - err = avdtp_close(sink->session, sink->stream); - if (err == 0) - return; - } - break; - case AVDTP_STATE_STREAMING: - if (state == AVDTP_STATE_OPEN) { - err = avdtp_suspend(sink->session, sink->stream); - if (err == 0) { - sink->suspending = TRUE; - return; - } - } - else if (state == AVDTP_STATE_IDLE) { - err = avdtp_close(sink->session, sink->stream); - if (err == 0) - return; - } - break; - default: - goto failed; - } - -failed: - error("%s: Error changing states", dev->path); -} - avdtp_state_t sink_get_state(struct device *dev) { struct sink *sink = dev->sink; diff --git a/audio/sink.h b/audio/sink.h index 7894a36c..309379b1 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -25,10 +25,7 @@ struct sink *sink_init(struct device *dev); void sink_free(struct device *dev); -int sink_get_config(struct device *dev, int sock, struct ipc_packet *req, - int pkt_len, struct ipc_data_cfg **rsp, int *fd); gboolean sink_is_active(struct device *dev); -void sink_set_state(struct device *dev, avdtp_state_t state); avdtp_state_t sink_get_state(struct device *dev); gboolean sink_new_stream(struct device *dev, struct avdtp *session, struct avdtp_stream *stream); diff --git a/audio/unix.c b/audio/unix.c index 52b27809..10856b93 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -43,21 +43,51 @@ #include "ipc.h" #include "device.h" #include "manager.h" +#include "avdtp.h" +#include "a2dp.h" +#include "headset.h" +#include "sink.h" #include "unix.h" +typedef enum { + TYPE_NONE, + TYPE_HEADSET, + TYPE_SINK, + TYPE_SOURCE +} service_type_t; + +typedef void (*notify_cb_t) (struct device *dev, void *data); + struct unix_client { struct device *dev; + service_type_t type; + union { + struct avdtp *session; + void *data; + } data; int sock; + int req_id; + notify_cb_t disconnect; + notify_cb_t suspend; + notify_cb_t play; }; static GSList *clients = NULL; static int unix_sock = -1; -static int unix_send_state(int sock, struct ipc_packet *pkt); - static void client_free(struct unix_client *client) { + switch (client->type) { + case TYPE_SINK: + case TYPE_SOURCE: + if (client->data.session) + avdtp_unref(client->data.session); + break; + default: + break; + } + if (client->sock >= 0) close(client->sock); g_free(client); @@ -96,12 +126,116 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) return sendmsg(sock, &msgh, MSG_NOSIGNAL); } +static service_type_t select_service(struct device *dev) +{ + if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) + return TYPE_SINK; + else if (dev->headset && headset_is_active(dev)) + return TYPE_HEADSET; + else if (dev->sink) + return TYPE_SINK; + else if (dev->headset) + return TYPE_HEADSET; + else + return TYPE_NONE; +} + +static void a2dp_setup_complete(struct avdtp *session, struct device *dev, + struct avdtp_stream *stream, + void *user_data) +{ + struct unix_client *client = user_data; + char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)]; + struct ipc_data_cfg *cfg = (void *) buf; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap; + struct sbc_codec_cap *sbc_cap; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + int fd; + GSList *caps; + + if (!stream) + goto failed; + + if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { + error("Unable to get stream transport"); + goto failed; + } + + for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { + cap = caps->data; + if (cap->category == AVDTP_MEDIA_CODEC) { + codec_cap = (void *) cap->data; + break; + } + } + + if (codec_cap == NULL || + codec_cap->media_codec_type != A2DP_CODEC_SBC) { + error("Unable to find matching codec capability"); + goto failed; + } + + cfg->fd_opt = CFG_FD_OPT_WRITE; + + sbc_cap = (void *) codec_cap; + cfg->channels = sbc_cap->channel_mode == A2DP_CHANNEL_MODE_MONO ? + 1 : 2; + cfg->channel_mode = sbc_cap->channel_mode; + cfg->sample_size = 2; + + switch (sbc_cap->frequency) { + case A2DP_SAMPLING_FREQ_16000: + cfg->rate = 16000; + break; + case A2DP_SAMPLING_FREQ_32000: + cfg->rate = 32000; + break; + case A2DP_SAMPLING_FREQ_44100: + cfg->rate = 44100; + break; + case A2DP_SAMPLING_FREQ_48000: + cfg->rate = 48000; + break; + } + + cfg->codec = CFG_CODEC_SBC; + sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? + 0x01 : 0x00; + sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; + + switch (sbc_cap->block_length) { + case A2DP_BLOCK_LENGTH_4: + sbc->blocks = 4; + break; + case A2DP_BLOCK_LENGTH_8: + sbc->blocks = 8; + break; + case A2DP_BLOCK_LENGTH_12: + sbc->blocks = 12; + break; + case A2DP_BLOCK_LENGTH_16: + sbc->blocks = 16; + break; + } + + sbc->bitpool = sbc_cap->max_bitpool; + + unix_send_cfg(client->sock, cfg, fd); + + return; + +failed: + unix_send_cfg(client->sock, NULL, -1); + a2dp_source_unlock(dev, session); +} + static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) { struct ipc_data_cfg *rsp; struct device *dev; - int ret, fd; + int ret, fd, id; dev = manager_get_connected_device(); if (dev) @@ -112,19 +246,50 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, goto failed; proceed: - ret = device_get_config(dev, client->sock, pkt, len, &rsp, &fd); - if (ret < 0) + client->type = select_service(dev); + + switch (client->type) { + case TYPE_SINK: + if (!client->data.session) + client->data.session = avdtp_get(&dev->src, &dev->dst); + + if (!a2dp_source_lock(dev, client->data.session)) { + error("Unable to lock A2DP source SEP"); + goto failed; + } + + id = a2dp_source_request_stream(client->data.session, dev, + TRUE, a2dp_setup_complete, + client); + if (id < 0) { + error("request_stream failed"); + goto failed; + } + + client->req_id = id; + client->disconnect = (notify_cb_t) a2dp_source_unlock; + client->suspend = (notify_cb_t) a2dp_source_suspend; + client->play = (notify_cb_t) a2dp_source_start_stream; + + break; + case TYPE_HEADSET: + if (!headset_lock(dev, client->data.data)) { + error("Unable to lock headset"); + goto failed; + } + + ret = headset_get_config(dev, client->sock, pkt, len, &rsp, + &fd); + client->disconnect = (notify_cb_t) headset_unlock; + client->suspend = (notify_cb_t) headset_suspend; + client->play = (notify_cb_t) headset_play; + break; + default: + error("No known services for device"); goto failed; + } client->dev = dev; - - /* Connecting in progress */ - if (ret == 1) - return; - - unix_send_cfg(client->sock, rsp, fd); - g_free(rsp); - return; failed: @@ -139,6 +304,7 @@ static void ctl_event(struct unix_client *client, struct ipc_packet *pkt, static void state_event(struct unix_client *client, struct ipc_packet *pkt, int len) { +#if 0 struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; struct device *dev = client->dev; @@ -148,6 +314,7 @@ static void state_event(struct unix_client *client, struct ipc_packet *pkt, state->state = device_get_state(dev); unix_send_state(client->sock, pkt); +#endif } static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) @@ -156,14 +323,30 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) struct ipc_packet *pkt = (void *) buf; struct unix_client *client = data; int len, len_check; + void *cb_data; if (cond & G_IO_NVAL) return FALSE; + switch (client->type) { + case TYPE_HEADSET: + cb_data = client->data.data; + break; + case TYPE_SINK: + case TYPE_SOURCE: + cb_data = client->data.session; + break; + default: + cb_data = NULL; + break; + } + if (cond & (G_IO_HUP | G_IO_ERR)) { debug("Unix client disconnected"); - if (client->dev) - device_set_state(client->dev, STATE_CONNECTED); + if (client->disconnect) + client->disconnect(client->dev, cb_data); + if (client->type == TYPE_SINK && client->req_id >= 0) + a2dp_source_cancel_stream(client->req_id); goto failed; } @@ -339,6 +522,7 @@ int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) return 0; } +#if 0 static int unix_send_state(int sock, struct ipc_packet *pkt) { struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; @@ -359,3 +543,4 @@ static int unix_send_state(int sock, struct ipc_packet *pkt) return 0; } +#endif -- cgit From 56a227224b72af03e4299db59805b361f89e2a5c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 16:18:35 +0000 Subject: Remove unused code --- audio/a2dp.c | 87 ------------------------------------------------------------ audio/a2dp.h | 3 --- 2 files changed, 90 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 5302fb2d..ceb06164 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -713,93 +713,6 @@ gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) return TRUE; } -gboolean a2dp_get_config(struct avdtp_stream *stream, - struct ipc_data_cfg **cfg, int *fd) -{ - struct avdtp_service_capability *cap; - struct avdtp_media_codec_capability *codec_cap = NULL; - struct sbc_codec_cap *sbc_cap; - struct ipc_data_cfg *rsp; - struct ipc_codec_sbc *sbc; - GSList *caps; - - rsp = g_malloc0(sizeof(struct ipc_data_cfg) + - sizeof(struct ipc_codec_sbc)); - *fd = -1; - sbc = (void *) rsp->data; - - if (!avdtp_stream_get_transport(stream, fd, &rsp->pkt_len, - &caps)) { - g_free(rsp); - return FALSE; - } - - for (; caps; caps = g_slist_next(caps)) { - cap = caps->data; - if (cap->category == AVDTP_MEDIA_CODEC) { - codec_cap = (void *) cap->data; - break; - } - } - - if (codec_cap == NULL) { - g_free(rsp); - return FALSE; - } - - rsp->fd_opt = CFG_FD_OPT_WRITE; - - *cfg = rsp; - - if (codec_cap->media_codec_type != A2DP_CODEC_SBC) - return TRUE; - - sbc_cap = (struct sbc_codec_cap *) codec_cap; - rsp->channels = sbc_cap->channel_mode == - A2DP_CHANNEL_MODE_MONO ? 1 : 2; - rsp->channel_mode = sbc_cap->channel_mode; - rsp->sample_size = 2; - - switch (sbc_cap->frequency) { - case A2DP_SAMPLING_FREQ_16000: - rsp->rate = 16000; - break; - case A2DP_SAMPLING_FREQ_32000: - rsp->rate = 32000; - break; - case A2DP_SAMPLING_FREQ_44100: - rsp->rate = 44100; - break; - case A2DP_SAMPLING_FREQ_48000: - rsp->rate = 48000; - break; - } - - rsp->codec = CFG_CODEC_SBC; - sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? - 0x01 : 0x00; - sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; - - switch (sbc_cap->block_length) { - case A2DP_BLOCK_LENGTH_4: - sbc->blocks = 4; - break; - case A2DP_BLOCK_LENGTH_8: - sbc->blocks = 8; - break; - case A2DP_BLOCK_LENGTH_12: - sbc->blocks = 12; - break; - case A2DP_BLOCK_LENGTH_16: - sbc->blocks = 16; - break; - } - - sbc->bitpool = sbc_cap->max_bitpool; - - return TRUE; -} - static void discovery_complete(struct avdtp *session, GSList *seps, int err, void *user_data) { diff --git a/audio/a2dp.h b/audio/a2dp.h index 3617afc4..c69b1e34 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -68,9 +68,6 @@ void a2dp_exit(void); gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); -gboolean a2dp_get_config(struct avdtp_stream *stream, - struct ipc_data_cfg **cfg, int *fd); - int a2dp_source_request_stream(struct avdtp *session, struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data); -- cgit From c2d6dc7eb5e2df982df34bb79c92ff5c4131489e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 16:21:57 +0000 Subject: Remove unused include --- audio/a2dp.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index ceb06164..14c1b440 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -36,7 +36,6 @@ #include "logging.h" #include "manager.h" -#include "ipc.h" #include "device.h" #include "avdtp.h" #include "sink.h" -- cgit From 5d5f4d99e9176da5f01b7195e6132b5796d026d8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 16:24:18 +0000 Subject: a2dp_select_capabilities doesn't need to be public --- audio/a2dp.c | 2 +- audio/a2dp.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 14c1b440..bcd294da 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -686,7 +686,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, return TRUE; } -gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) +static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) { struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap, *acp_sbc; diff --git a/audio/a2dp.h b/audio/a2dp.h index c69b1e34..cf13de8d 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -66,8 +66,6 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source); void a2dp_exit(void); -gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps); - int a2dp_source_request_stream(struct avdtp *session, struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data); -- cgit From 9ce01693dd0aa7407b7d8eb37c8524dd05abe15c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 16:59:27 +0000 Subject: Free callback data after removing it from the list --- audio/a2dp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index bcd294da..d1f90fd1 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -774,6 +774,7 @@ gboolean a2dp_source_cancel_stream(int id) return FALSE; setup->cb = g_slist_remove(setup->cb, cb_data); + g_free(cb_data); if (!setup->cb) setup->canceled = TRUE; -- cgit From 8ae40e0b0046a011bbc15771f248d7950a606ae4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 19:14:31 +0000 Subject: Fix minumum bitpool calculation --- audio/a2dp.c | 3 +-- audio/avdtp.c | 41 +++++++++++++++++++++++++++++------------ audio/avdtp.h | 2 +- audio/sink.c | 10 ++++++---- audio/unix.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 19 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index d1f90fd1..bc9c4013 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -675,8 +675,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, else if (supported->allocation_method & A2DP_ALLOCATION_SNR) cap->allocation_method = A2DP_ALLOCATION_SNR; - min_bitpool = MAX(default_bitpool(cap->frequency, cap->channel_mode), - supported->min_bitpool); + min_bitpool = MAX(2, supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), supported->max_bitpool); diff --git a/audio/avdtp.c b/audio/avdtp.c index 7194177f..d2733d6e 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -218,6 +218,11 @@ struct avdtp_local_sep { void *data; }; +struct stream_callback { + avdtp_stream_state_cb cb; + void *user_data; +}; + struct avdtp_stream { int sock; uint16_t mtu; @@ -225,9 +230,8 @@ struct avdtp_stream { struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; + GSList *callbacks; struct avdtp_service_capability *codec; - avdtp_stream_state_cb cb; - void *user_data; guint io; /* Transport GSource ID */ guint timer; /* Waiting for other side to close or open the transport channel */ @@ -456,10 +460,13 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); - if (stream->caps) { - g_slist_foreach(stream->caps, (GFunc) g_free, NULL); - g_slist_free(stream->caps); - } + + g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); + g_slist_free(stream->callbacks); + + g_slist_foreach(stream->caps, (GFunc) g_free, NULL); + g_slist_free(stream->caps); + g_free(stream); } @@ -485,9 +492,14 @@ static void avdtp_sep_set_state(struct avdtp *session, old_state = sep->state; sep->state = state; - if (stream && stream->cb) - stream->cb(stream, old_state, state, err_ptr, - stream->user_data); + if (stream) { + GSList *l; + for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { + struct stream_callback *cb = l->data; + cb->cb(stream, old_state, state, err_ptr, + cb->user_data); + } + } if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); @@ -2085,11 +2097,16 @@ int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, return -EINVAL; } -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, +void avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data) { - stream->cb = cb; - stream->user_data = data; + struct stream_callback *stream_cb; + + stream_cb = g_new(struct stream_callback, 1); + stream_cb->cb = cb; + stream_cb->user_data = data; + + stream->callbacks = g_slist_append(stream->callbacks, stream_cb);; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index 0043c480..aa835131 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -165,7 +165,7 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, +void avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, diff --git a/audio/sink.c b/audio/sink.c index 0f69bbf6..fd6e9db2 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -71,9 +71,11 @@ static void pending_request_free(struct pending_request *pending) g_free(pending); } -void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, void *user_data) +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) { struct device *dev = user_data; struct sink *sink = dev->sink; @@ -315,7 +317,7 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->stream = stream; sink->initiator = FALSE; - avdtp_stream_set_cb(session, stream, stream_state_changed, dev); + avdtp_stream_add_cb(session, stream, stream_state_changed, dev); return TRUE; } diff --git a/audio/unix.c b/audio/unix.c index 10856b93..0e5fd5c1 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -140,6 +140,30 @@ static service_type_t select_service(struct device *dev) return TYPE_NONE; } + +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) +{ + struct unix_client *client = user_data; + + if (err) + return; + + switch (new_state) { + case AVDTP_STATE_IDLE: + if (client->data.session) { + avdtp_unref(client->data.session); + client->data.session = NULL; + } + break; + default: + break; + } +} + static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data) @@ -223,11 +247,15 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, unix_send_cfg(client->sock, cfg, fd); + avdtp_stream_add_cb(session, stream, stream_state_changed, dev); + return; failed: unix_send_cfg(client->sock, NULL, -1); a2dp_source_unlock(dev, session); + avdtp_unref(client->data.session); + client->data.session = NULL; } static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, -- cgit From 871c0518ec53309a38debbb86a36c035c3470fba Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 19:24:07 +0000 Subject: Revert changes from previous commit that were not supposed to go in --- audio/avdtp.c | 41 ++++++++++++----------------------------- audio/avdtp.h | 2 +- audio/sink.c | 10 ++++------ audio/unix.c | 28 ---------------------------- 4 files changed, 17 insertions(+), 64 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index d2733d6e..7194177f 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -218,11 +218,6 @@ struct avdtp_local_sep { void *data; }; -struct stream_callback { - avdtp_stream_state_cb cb; - void *user_data; -}; - struct avdtp_stream { int sock; uint16_t mtu; @@ -230,8 +225,9 @@ struct avdtp_stream { struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; - GSList *callbacks; struct avdtp_service_capability *codec; + avdtp_stream_state_cb cb; + void *user_data; guint io; /* Transport GSource ID */ guint timer; /* Waiting for other side to close or open the transport channel */ @@ -460,13 +456,10 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); - - g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); - g_slist_free(stream->callbacks); - - g_slist_foreach(stream->caps, (GFunc) g_free, NULL); - g_slist_free(stream->caps); - + if (stream->caps) { + g_slist_foreach(stream->caps, (GFunc) g_free, NULL); + g_slist_free(stream->caps); + } g_free(stream); } @@ -492,14 +485,9 @@ static void avdtp_sep_set_state(struct avdtp *session, old_state = sep->state; sep->state = state; - if (stream) { - GSList *l; - for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { - struct stream_callback *cb = l->data; - cb->cb(stream, old_state, state, err_ptr, - cb->user_data); - } - } + if (stream && stream->cb) + stream->cb(stream, old_state, state, err_ptr, + stream->user_data); if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); @@ -2097,16 +2085,11 @@ int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, return -EINVAL; } -void avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, +void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data) { - struct stream_callback *stream_cb; - - stream_cb = g_new(struct stream_callback, 1); - stream_cb->cb = cb; - stream_cb->user_data = data; - - stream->callbacks = g_slist_append(stream->callbacks, stream_cb);; + stream->cb = cb; + stream->user_data = data; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index aa835131..0043c480 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -165,7 +165,7 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); -void avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, +void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, diff --git a/audio/sink.c b/audio/sink.c index fd6e9db2..0f69bbf6 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -71,11 +71,9 @@ static void pending_request_free(struct pending_request *pending) g_free(pending); } -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) +void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, void *user_data) { struct device *dev = user_data; struct sink *sink = dev->sink; @@ -317,7 +315,7 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->stream = stream; sink->initiator = FALSE; - avdtp_stream_add_cb(session, stream, stream_state_changed, dev); + avdtp_stream_set_cb(session, stream, stream_state_changed, dev); return TRUE; } diff --git a/audio/unix.c b/audio/unix.c index 0e5fd5c1..10856b93 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -140,30 +140,6 @@ static service_type_t select_service(struct device *dev) return TYPE_NONE; } - -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) -{ - struct unix_client *client = user_data; - - if (err) - return; - - switch (new_state) { - case AVDTP_STATE_IDLE: - if (client->data.session) { - avdtp_unref(client->data.session); - client->data.session = NULL; - } - break; - default: - break; - } -} - static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data) @@ -247,15 +223,11 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, unix_send_cfg(client->sock, cfg, fd); - avdtp_stream_add_cb(session, stream, stream_state_changed, dev); - return; failed: unix_send_cfg(client->sock, NULL, -1); a2dp_source_unlock(dev, session); - avdtp_unref(client->data.session); - client->data.session = NULL; } static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, -- cgit From 0f458da19471c933a4105cc450c48548b72edc8e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 21:48:33 +0000 Subject: Handle error situations better --- audio/a2dp.c | 64 +++++++++++++++------- audio/a2dp.h | 3 +- audio/avdtp.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++----------- audio/avdtp.h | 31 +++++++---- audio/sink.c | 16 +++--- audio/unix.c | 82 +++++++++++++++++++++++------ 6 files changed, 280 insertions(+), 82 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index bc9c4013..2ad660b6 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -190,9 +190,10 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, + struct avdtp_error *err) { - int err; + int ret; if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Cfm"); @@ -201,15 +202,22 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Source: Set_Configuration_Cfm"); + if (err) { + source.stream = NULL; + if (setup) + finalize_stream_setup(setup); + return; + } + source.stream = stream; if (!setup) return; - err = avdtp_open(session, stream); - if (err < 0) { - error("Error on avdtp_open %s (%d)", strerror(-err), - -err); + ret = avdtp_open(session, stream); + if (ret < 0) { + error("Error on avdtp_open %s (%d)", strerror(-ret), + -ret); setup->stream = FALSE; finalize_stream_setup(setup); } @@ -226,7 +234,7 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Set_Configuration_Cfm"); @@ -245,7 +253,7 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Open_Cfm"); @@ -256,11 +264,17 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (setup->canceled) { - avdtp_close(session, stream); + if (!err) + avdtp_close(session, stream); stream_setup_free(setup); return; } + if (err) { + setup->stream = NULL; + goto finalize; + } + if (setup->start) { if (avdtp_start(session, stream) == 0) return; @@ -269,6 +283,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, setup->stream = NULL; } +finalize: finalize_stream_setup(setup); } @@ -287,7 +302,7 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Start_Cfm"); @@ -298,11 +313,15 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (setup->canceled) { - avdtp_close(session, stream); + if (!err) + avdtp_close(session, stream); stream_setup_free(setup); return; } + if (err) + setup->stream = NULL; + finalize_stream_setup(setup); } @@ -317,7 +336,7 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) { debug("SBC Sink: Suspend_Cfm"); @@ -328,6 +347,13 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, source.suspending = FALSE; + if (err) { + source.start_requested = FALSE; + if (setup) + finalize_stream_setup(setup); + return; + } + if (source.start_requested) { avdtp_start(session, stream); source.start_requested = FALSE; @@ -350,7 +376,7 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) { debug("SBC Sink: Close_Cfm"); @@ -378,7 +404,7 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream) + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: Abort_Cfm"); @@ -396,7 +422,8 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } -static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep) +static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err) { if (sep == sink.sep) debug("SBC Sink: ReConfigure_Cfm"); @@ -781,7 +808,8 @@ gboolean a2dp_source_cancel_stream(int id) return TRUE; } -int a2dp_source_request_stream(struct avdtp *session, struct device *dev, +unsigned int a2dp_source_request_stream(struct avdtp *session, + struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data) { @@ -791,7 +819,7 @@ int a2dp_source_request_stream(struct avdtp *session, struct device *dev, cb_data = g_new(struct a2dp_stream_cb, 1); cb_data->cb = cb; cb_data->user_data = user_data; - cb_data->id = cb_id++; + cb_data->id = ++cb_id; if (setup) { setup->canceled = FALSE; @@ -839,7 +867,7 @@ int a2dp_source_request_stream(struct avdtp *session, struct device *dev, failed: stream_setup_free(setup); cb_id--; - return -1; + return 0; } gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) diff --git a/audio/a2dp.h b/audio/a2dp.h index cf13de8d..6d69189c 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -66,7 +66,8 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source); void a2dp_exit(void); -int a2dp_source_request_stream(struct avdtp *session, struct device *dev, +unsigned int a2dp_source_request_stream(struct avdtp *session, + struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data); gboolean a2dp_source_cancel_stream(int id); diff --git a/audio/avdtp.c b/audio/avdtp.c index 7194177f..1687714b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -218,6 +218,12 @@ struct avdtp_local_sep { void *data; }; +struct stream_callback { + avdtp_stream_state_cb cb; + void *user_data; + unsigned int id; +}; + struct avdtp_stream { int sock; uint16_t mtu; @@ -225,9 +231,8 @@ struct avdtp_stream { struct avdtp_local_sep *lsep; uint8_t rseid; GSList *caps; + GSList *callbacks; struct avdtp_service_capability *codec; - avdtp_stream_state_cb cb; - void *user_data; guint io; /* Transport GSource ID */ guint timer; /* Waiting for other side to close or open the transport channel */ @@ -456,10 +461,13 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); - if (stream->caps) { - g_slist_foreach(stream->caps, (GFunc) g_free, NULL); - g_slist_free(stream->caps); - } + + g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); + g_slist_free(stream->callbacks); + + g_slist_foreach(stream->caps, (GFunc) g_free, NULL); + g_slist_free(stream->caps); + g_free(stream); } @@ -485,9 +493,14 @@ static void avdtp_sep_set_state(struct avdtp *session, old_state = sep->state; sep->state = state; - if (stream && stream->cb) - stream->cb(stream, old_state, state, err_ptr, - stream->user_data); + if (stream) { + GSList *l; + for (l = stream->callbacks; l != NULL; l = g_slist_next(l)) { + struct stream_callback *cb = l->data; + cb->cb(stream, old_state, state, err_ptr, + cb->user_data); + } + } if (state == AVDTP_STATE_IDLE) { session->streams = g_slist_remove(session->streams, stream); @@ -1170,7 +1183,7 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(stream->session, sep, stream); + sep->cfm->close(stream->session, sep, stream, NULL); avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); @@ -1195,7 +1208,7 @@ static void handle_transport_connect(struct avdtp *session, int sock, stream->mtu = mtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream); + sep->cfm->open(session, sep, stream, NULL); channel = g_io_channel_unix_new(stream->sock); @@ -1465,33 +1478,75 @@ static gboolean request_timeout(gpointer user_data) struct avdtp *session = user_data; struct pending_req *req; struct seid_req sreq; - struct avdtp_remote_sep *sep; + struct avdtp_remote_sep *rsep; + struct avdtp_local_sep *lsep; struct avdtp_stream *stream; uint8_t seid; - - error("Request timed out"); + struct avdtp_error err; req = session->req; session->req = NULL; - switch (req->msg->signal_id) { - case AVDTP_DISCOVER: - case AVDTP_GET_CAPABILITIES: - case AVDTP_SET_CONFIGURATION: - case AVDTP_ABORT: - goto failed; - } + avdtp_error_init(&err, AVDTP_ERROR_ERRNO, ETIMEDOUT); seid = ((struct seid_req *) (req->msg))->acp_seid; - sep = find_remote_sep(session->seps, seid); - if (!sep) { + rsep = find_remote_sep(session->seps, seid); + if (!rsep) { error("Unable to find matching remote SEID %u", seid); goto failed; } stream = find_stream_by_rseid(session, seid); + if (stream) + lsep = stream->lsep; + else + lsep = NULL; + + switch (req->msg->signal_id) { + case AVDTP_RECONFIGURE: + error("Reconfigure request timed out"); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &err); + break; + case AVDTP_OPEN: + error("Open request timed out"); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &err); + break; + case AVDTP_START: + error("Start request timed out"); + if (lsep && lsep->cfm && lsep->cfm->start) + lsep->cfm->start(session, lsep, stream, &err); + break; + case AVDTP_SUSPEND: + error("Suspend request timed out"); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &err); + break; + case AVDTP_CLOSE: + error("Close request timed out"); + if (lsep && lsep->cfm && lsep->cfm->close) + lsep->cfm->close(session, lsep, stream, &err); + break; + case AVDTP_SET_CONFIGURATION: + error("SetConfiguration request timed out"); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + lsep->cfm->set_configuration(session, lsep, stream, &err); + /* fallthrough on purpose */ + case AVDTP_DISCOVER: + error("Discover request timed out"); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + goto failed; + case AVDTP_GET_CAPABILITIES: + error("GetCapabilities request timed out"); + goto failed; + case AVDTP_ABORT: + error("Abort request timed out"); + goto failed; + } + memset(&sreq, 0, sizeof(sreq)); init_request(&sreq.header, AVDTP_ABORT); sreq.acp_seid = seid; @@ -1646,7 +1701,7 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream); + sep->cfm->set_configuration(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); @@ -1684,7 +1739,7 @@ static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream); + sep->cfm->start(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); @@ -1715,7 +1770,7 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream); + sep->cfm->suspend(session, sep, stream, NULL); return TRUE; } @@ -1727,7 +1782,7 @@ static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream); + sep->cfm->suspend(session, sep, stream, NULL); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -1845,6 +1900,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre { struct avdtp_error err; uint8_t acp_seid, category; + struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; switch (header->signal_id) { case AVDTP_DISCOVER: @@ -1864,42 +1920,56 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre return FALSE; error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, &err); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err((void *) header, size, &err, &category)) return FALSE; error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(session, sep, stream, &err); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err((void *) header, size, &err, &category)) return FALSE; error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->reconfigure) + sep->cfm->reconfigure(session, sep, stream, &err); return TRUE; case AVDTP_START: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->start) + sep->cfm->start(session, sep, stream, &err); return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(session, sep, stream, &err); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->close) + sep->cfm->close(session, sep, stream, &err); return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) return FALSE; error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, &err); return TRUE; default: error("Unknown reject response signal id: %u", @@ -2085,11 +2155,45 @@ int avdtp_get_seps(struct avdtp *session, uint8_t acp_type, uint8_t media_type, return -EINVAL; } -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data) +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id) { - stream->cb = cb; - stream->user_data = data; + GSList *l; + struct stream_callback *cb; + + for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { + struct stream_callback *tmp = l->data; + if (tmp->id == id) { + cb = tmp; + break; + } + } + + if (!cb) + return FALSE; + + stream->callbacks = g_slist_remove(stream->callbacks, cb); + g_free(cb); + + return TRUE; +} + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data) +{ + struct stream_callback *stream_cb; + static unsigned int id = 0; + + stream_cb = g_new(struct stream_callback, 1); + stream_cb->cb = cb; + stream_cb->user_data = data; + stream_cb->id = ++id; + + stream->callbacks = g_slist_append(stream->callbacks, stream_cb);; + + return stream_cb->id; } int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index 0043c480..49907cff 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -96,22 +96,29 @@ typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, struct avdtp_error *err); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, struct avdtp_error *err); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream); + struct avdtp_stream *stream, + struct avdtp_error *err); void (*reconfigure) (struct avdtp *session, - struct avdtp_local_sep *lsep); + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err); }; /* Callbacks for indicating when we received a new command. The return value @@ -165,8 +172,12 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); -void avdtp_stream_set_cb(struct avdtp *session, struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data); +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data); +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *mtu, GSList **caps); diff --git a/audio/sink.c b/audio/sink.c index 0f69bbf6..ab9f401b 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -48,7 +48,7 @@ struct pending_request { DBusConnection *conn; DBusMessage *msg; - int id; + unsigned int id; }; struct sink { @@ -71,9 +71,11 @@ static void pending_request_free(struct pending_request *pending) g_free(pending); } -void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, void *user_data) +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) { struct device *dev = user_data; struct sink *sink = dev->sink; @@ -153,7 +155,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, struct device *dev = data; struct sink *sink = dev->sink; struct pending_request *pending; - int id; + unsigned int id; if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); @@ -171,7 +173,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, id = a2dp_source_request_stream(sink->session, dev, FALSE, stream_setup_complete, pending); - if (id < 0) { + if (id == 0) { pending_request_free(pending); sink->connect = NULL; avdtp_unref(sink->session); @@ -315,7 +317,7 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->stream = stream; sink->initiator = FALSE; - avdtp_stream_set_cb(session, stream, stream_state_changed, dev); + avdtp_stream_add_cb(session, stream, stream_state_changed, dev); return TRUE; } diff --git a/audio/unix.c b/audio/unix.c index 10856b93..94c84500 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -58,15 +58,21 @@ typedef enum { typedef void (*notify_cb_t) (struct device *dev, void *data); +struct a2dp_data { + struct avdtp *session; + struct avdtp_stream *stream; +}; + struct unix_client { struct device *dev; service_type_t type; union { - struct avdtp *session; + struct a2dp_data a2dp; void *data; - } data; + } d; int sock; - int req_id; + unsigned int req_id; + unsigned int cb_id; notify_cb_t disconnect; notify_cb_t suspend; notify_cb_t play; @@ -78,11 +84,17 @@ static int unix_sock = -1; static void client_free(struct unix_client *client) { + struct a2dp_data *a2dp; + switch (client->type) { case TYPE_SINK: case TYPE_SOURCE: - if (client->data.session) - avdtp_unref(client->data.session); + a2dp = &client->d.a2dp; + if (client->cb_id > 0) + avdtp_stream_remove_cb(a2dp->session, a2dp->stream, + client->cb_id); + if (a2dp->session); + avdtp_unref(a2dp->session); break; default: break; @@ -140,6 +152,30 @@ static service_type_t select_service(struct device *dev) return TYPE_NONE; } + +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) +{ + struct unix_client *client = user_data; + struct a2dp_data *a2dp = &client->d.a2dp; + + switch (new_state) { + case AVDTP_STATE_IDLE: + if (a2dp->session) { + avdtp_unref(a2dp->session); + a2dp->session = NULL; + } + a2dp->stream = NULL; + client->cb_id = 0; + break; + default: + break; + } +} + static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data) @@ -151,12 +187,17 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; struct ipc_codec_sbc *sbc = (void *) cfg->data; + struct a2dp_data *a2dp = &client->d.a2dp; int fd; GSList *caps; + client->req_id = 0; + if (!stream) goto failed; + a2dp->stream = stream; + if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { error("Unable to get stream transport"); goto failed; @@ -223,11 +264,18 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, unix_send_cfg(client->sock, cfg, fd); + client->cb_id = avdtp_stream_add_cb(session, stream, + stream_state_changed, client); + return; failed: + error("stream setup failed"); unix_send_cfg(client->sock, NULL, -1); a2dp_source_unlock(dev, session); + avdtp_unref(a2dp->session); + a2dp->session = NULL; + a2dp->stream = NULL; } static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, @@ -235,7 +283,9 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, { struct ipc_data_cfg *rsp; struct device *dev; - int ret, fd, id; + int ret, fd; + unsigned int id; + struct a2dp_data *a2dp; dev = manager_get_connected_device(); if (dev) @@ -250,18 +300,20 @@ proceed: switch (client->type) { case TYPE_SINK: - if (!client->data.session) - client->data.session = avdtp_get(&dev->src, &dev->dst); + a2dp = &client->d.a2dp; + + if (!a2dp->session) + a2dp->session = avdtp_get(&dev->src, &dev->dst); - if (!a2dp_source_lock(dev, client->data.session)) { + if (!a2dp_source_lock(dev, a2dp->session)) { error("Unable to lock A2DP source SEP"); goto failed; } - id = a2dp_source_request_stream(client->data.session, dev, + id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client); - if (id < 0) { + if (id == 0) { error("request_stream failed"); goto failed; } @@ -273,7 +325,7 @@ proceed: break; case TYPE_HEADSET: - if (!headset_lock(dev, client->data.data)) { + if (!headset_lock(dev, client->d.data)) { error("Unable to lock headset"); goto failed; } @@ -330,11 +382,11 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) switch (client->type) { case TYPE_HEADSET: - cb_data = client->data.data; + cb_data = client->d.data; break; case TYPE_SINK: case TYPE_SOURCE: - cb_data = client->data.session; + cb_data = client->d.a2dp.session; break; default: cb_data = NULL; @@ -345,7 +397,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) debug("Unix client disconnected"); if (client->disconnect) client->disconnect(client->dev, cb_data); - if (client->type == TYPE_SINK && client->req_id >= 0) + if (client->type == TYPE_SINK && client->req_id > 0) a2dp_source_cancel_stream(client->req_id); goto failed; } -- cgit From 2c0e2fcf82a062558526ae164796855b584603f0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 16 Aug 2007 22:05:55 +0000 Subject: Fix path (un)registering when hcid is not running --- audio/manager.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 231af376..20b91435 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1670,18 +1670,18 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, enabled = enable; - if (headset_server_init(conn, no_hfp) < 0) - goto failed; - - if (a2dp_init(conn, enable->sink, enable->source) < 0) - goto failed; - if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, NULL, manager_unregister)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); goto failed; } + if (headset_server_init(conn, no_hfp) < 0) + goto failed; + + if (a2dp_init(conn, enable->sink, enable->source) < 0) + goto failed; + if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, manager_methods, -- cgit From ab622e0de1826968a0506d47dc766a59787074bc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 17 Aug 2007 12:06:38 +0000 Subject: Fix seid counting in suspend and start requests --- audio/avdtp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 1687714b..32f67fce 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -941,7 +941,7 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, return FALSE; } - seid_count = 1 + (sizeof(struct start_req) - size); + seid_count = 1 + size - sizeof(struct start_req); seid = &req->first_seid; @@ -1048,7 +1048,7 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, return FALSE; } - seid_count = 1 + (sizeof(struct suspend_req) - size); + seid_count = 1 + size - sizeof(struct suspend_req); seid = &req->first_seid; -- cgit From c2f12da34adf0e5d2a04b7b3dbeb3753cb77f5d7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 17 Aug 2007 16:21:23 +0000 Subject: Add some debug for the stream endpoint discovery --- audio/avdtp.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 32f67fce..443f096b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1295,15 +1295,15 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, switch(header->message_type) { case AVDTP_MSG_TYPE_ACCEPT: - if (!avdtp_parse_resp(session, session->req->stream, header, - size)) { + if (!avdtp_parse_resp(session, session->req->stream, + header, size)) { error("Unable to parse accept response"); goto failed; } break; case AVDTP_MSG_TYPE_REJECT: - if (!avdtp_parse_rej(session, session->req->stream, header, - size)) { + if (!avdtp_parse_rej(session, session->req->stream, + header, size)) { error("Unable to parse reject response"); goto failed; } @@ -1627,6 +1627,10 @@ static gboolean avdtp_discover_resp(struct avdtp *session, struct seid_req req; int ret; + debug("seid %d type %d media %d in use %d", + resp->seps[i].seid, resp->seps[i].type, + resp->seps[i].media_type, resp->seps[i].inuse); + /* Skip SEP's which are in use */ if (resp->seps[i].inuse) continue; @@ -1680,6 +1684,9 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session, sep = find_remote_sep(session->seps, seid); + debug("seid %d type %d media %d", sep->seid, + sep->type, sep->media_type); + if (sep->caps) { g_slist_foreach(sep->caps, (GFunc) g_free, NULL); g_slist_free(sep->caps); @@ -1810,8 +1817,8 @@ static gboolean avdtp_parse_resp(struct avdtp *session, return avdtp_discover_resp(session, (void *) header, size); case AVDTP_GET_CAPABILITIES: debug("GET_CAPABILITIES request succeeded"); - if (!avdtp_get_capabilities_resp(session, (void *) header, - size)) + if (!avdtp_get_capabilities_resp(session, + (void *) header, size)) return FALSE; if (!(next && next->signal_id == AVDTP_GET_CAPABILITIES)) finalize_discovery(session, 0); @@ -1822,27 +1829,24 @@ static gboolean avdtp_parse_resp(struct avdtp *session, (void *) header, size); case AVDTP_RECONFIGURE: debug("RECONFIGURE request succeeded"); - return avdtp_reconfigure_resp(session, stream, (void *) header, - size); + return avdtp_reconfigure_resp(session, stream, + (void *) header, size); case AVDTP_OPEN: debug("OPEN request succeeded"); return avdtp_open_resp(session, stream, (void *) header, size); case AVDTP_SUSPEND: debug("SUSPEND request succeeded"); - return avdtp_suspend_resp(session, stream, (void *) header, - size); + return avdtp_suspend_resp(session, stream, + (void *) header, size); case AVDTP_START: debug("START request succeeded"); - return avdtp_start_resp(session, stream, (void *) header, - size); + return avdtp_start_resp(session, stream, (void *) header, size); case AVDTP_CLOSE: debug("CLOSE request succeeded"); - return avdtp_close_resp(session, stream, (void *) header, - size); + return avdtp_close_resp(session, stream, (void *) header, size); case AVDTP_ABORT: debug("ABORT request succeeded"); - return avdtp_abort_resp(session, stream, (void *) header, - size); + return avdtp_abort_resp(session, stream, (void *) header, size); } error("Unknown signal id in accept response: %u", header->signal_id); @@ -1896,7 +1900,7 @@ static gboolean stream_rej_to_err(struct stream_rej *rej, int size, } static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *header, int size) + struct avdtp_header *header, int size) { struct avdtp_error err; uint8_t acp_seid, category; -- cgit From c0e0aa5745b38505b180ffdd7d5df44612597e75 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Aug 2007 21:57:26 +0000 Subject: Accept start requests and add timeout for suspending unused streams --- audio/a2dp.c | 147 ++++++++++++++++++++++++++++++++++++++++++---------------- audio/avdtp.c | 8 +++- 2 files changed, 115 insertions(+), 40 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 2ad660b6..8206b880 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -41,6 +41,10 @@ #include "sink.h" #include "a2dp.h" +/* The duration that streams without users are allowed to stay in + * STREAMING state. */ +#define SUSPEND_TIMEOUT 5000 + #ifndef MIN # define MIN(x, y) ((x) < (y) ? (x) : (y)) #endif @@ -51,9 +55,11 @@ struct a2dp_sep { struct avdtp_local_sep *sep; + struct avdtp *session; struct avdtp_stream *stream; struct device *used_by; uint32_t record_id; + guint suspend_timer; gboolean start_requested; gboolean suspending; gboolean starting; @@ -76,11 +82,26 @@ struct a2dp_stream_setup { static DBusConnection *connection = NULL; -static struct a2dp_sep sink = { NULL, NULL, 0 }; -static struct a2dp_sep source = { NULL, NULL, 0 }; +static struct a2dp_sep sink = { NULL }; +static struct a2dp_sep source = { NULL }; static struct a2dp_stream_setup *setup = NULL; +static void stream_cleanup(struct a2dp_sep *sep) +{ + if (sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; + } + + if (sep->session) { + avdtp_unref(sep->session); + sep->session = NULL; + } + + sep->stream = NULL; +} + static void stream_setup_free(struct a2dp_stream_setup *s) { if (s->session) @@ -109,16 +130,18 @@ static gboolean setconf_ind(struct avdtp *session, GSList *caps, uint8_t *err, uint8_t *category) { + struct a2dp_sep *a2dp_sep; struct device *dev; bdaddr_t addr; if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Ind"); - return TRUE; + a2dp_sep = &sink; + } else { + debug("SBC Source: Set_Configuration_Ind"); + a2dp_sep = &source; } - debug("SBC Source: Set_Configuration_Ind"); - avdtp_get_peers(session, NULL, &addr); dev = manager_device_connected(&addr, A2DP_SOURCE_UUID); @@ -128,9 +151,10 @@ static gboolean setconf_ind(struct avdtp *session, return FALSE; } - source.stream = stream; + a2dp_sep->stream = stream; - sink_new_stream(dev, session, stream); + if (a2dp_sep == &source) + sink_new_stream(dev, session, stream); return TRUE; } @@ -193,23 +217,25 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err) { + struct a2dp_sep *a2dp_sep; int ret; if (sep == sink.sep) { debug("SBC Sink: Set_Configuration_Cfm"); - return; + a2dp_sep = &sink; + } else { + debug("SBC Source: Set_Configuration_Cfm"); + a2dp_sep = &source; } - debug("SBC Source: Set_Configuration_Cfm"); - if (err) { - source.stream = NULL; + a2dp_sep->stream = NULL; if (setup) finalize_stream_setup(setup); return; } - source.stream = stream; + a2dp_sep->stream = stream; if (!setup) return; @@ -287,18 +313,40 @@ finalize: finalize_stream_setup(setup); } +static gboolean suspend_timeout(struct a2dp_sep *sep) +{ + if (avdtp_suspend(sep->session, sep->stream) == 0) + sep->suspending = TRUE; + + sep->suspend_timer = FALSE; + + avdtp_unref(sep->session); + sep->session = NULL; + + return FALSE; +} + static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep; + + if (sep == sink.sep) { debug("SBC Sink: Start_Ind"); - else + a2dp_sep = &sink; + } + else { debug("SBC Source: Start_Ind"); + a2dp_sep = &source; + } - /* Refuse to go into streaming state since this action should only be - * initiated by alsa */ - *err = AVDTP_NOT_SUPPORTED_COMMAND; - return FALSE; + a2dp_sep->session = avdtp_ref(session); + + a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, + (GSourceFunc) suspend_timeout, + a2dp_sep); + + return TRUE; } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, @@ -338,39 +386,45 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err) { + struct a2dp_sep *a2dp_sep; + if (sep == sink.sep) { debug("SBC Sink: Suspend_Cfm"); - return; + a2dp_sep = &sink; + } else { + debug("SBC Source: Suspend_Cfm"); + a2dp_sep = &source; } - debug("SBC Source: Suspend_Cfm"); - - source.suspending = FALSE; + a2dp_sep->suspending = FALSE; if (err) { - source.start_requested = FALSE; + a2dp_sep->start_requested = FALSE; if (setup) finalize_stream_setup(setup); return; } - if (source.start_requested) { + if (a2dp_sep->start_requested) { avdtp_start(session, stream); - source.start_requested = FALSE; + a2dp_sep->start_requested = FALSE; } } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { + struct a2dp_sep *a2dp_sep; + if (sep == sink.sep) { debug("SBC Sink: Close_Ind"); - return TRUE; + a2dp_sep = &sink; + } else { + debug("SBC Source: Close_Ind"); + a2dp_sep = &source; } - debug("SBC Source: Close_Ind"); - - source.stream = NULL; + stream_cleanup(a2dp_sep); return TRUE; } @@ -378,27 +432,33 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err) { + struct a2dp_sep *a2dp_sep; + if (sep == sink.sep) { debug("SBC Sink: Close_Cfm"); - return; + a2dp_sep = &sink; + } else { + debug("SBC Source: Close_Cfm"); + a2dp_sep = &source; } - debug("SBC Source: Close_Cfm"); - - source.stream = NULL; + stream_cleanup(a2dp_sep); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err) { + struct a2dp_sep *a2dp_sep; + if (sep == sink.sep) { debug("SBC Sink: Abort_Ind"); - return TRUE; + a2dp_sep = &sink; + } else { + debug("SBC Source: Abort_Ind"); + a2dp_sep = &source; } - debug("SBC Source: Abort_Ind"); - - source.stream = NULL; + stream_cleanup(a2dp_sep); return TRUE; } @@ -406,10 +466,17 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep; + + if (sep == sink.sep) { debug("SBC Sink: Abort_Cfm"); - else + a2dp_sep = &sink; + } else { debug("SBC Source: Abort_Cfm"); + a2dp_sep = &source; + } + + stream_cleanup(a2dp_sep); } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -853,6 +920,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, break; case AVDTP_STATE_STREAMING: if (!start || !source.suspending) { + if (source.suspend_timer) + g_source_remove(source.suspend_timer); g_idle_add((GSourceFunc) finalize_stream_setup, setup); return cb_data->id; } diff --git a/audio/avdtp.c b/audio/avdtp.c index 443f096b..8348d3d6 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -524,11 +524,17 @@ static void finalize_discovery(struct avdtp *session, int err) static void release_stream(struct avdtp_stream *stream, struct avdtp *session) { + struct avdtp_local_sep *sep = stream->lsep; + if (stream->sock >= 0) close(stream->sock); if (stream->io) g_source_remove(stream->io); - avdtp_sep_set_state(session, stream->lsep, AVDTP_STATE_IDLE); + + if (sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, NULL); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); } static void connection_lost(struct avdtp *session, int err) -- cgit From 58b173171f61373c7eb8ed00d0635fb06fa741cc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Aug 2007 22:07:10 +0000 Subject: Reset value of timer id after removing it --- audio/a2dp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 8206b880..ce50c9cf 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -920,8 +920,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, break; case AVDTP_STATE_STREAMING: if (!start || !source.suspending) { - if (source.suspend_timer) + if (source.suspend_timer) { g_source_remove(source.suspend_timer); + source.suspend_timer = 0; + } g_idle_add((GSourceFunc) finalize_stream_setup, setup); return cb_data->id; } -- cgit From 1f6fd5d80c9227482a877e6eed1552d17f76a29d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Aug 2007 22:45:46 +0000 Subject: Add idle timeout for streams --- audio/avdtp.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 8348d3d6..42a59aa5 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -71,6 +71,7 @@ #define REQ_TIMEOUT 2000 #define DISCONNECT_TIMEOUT 5000 +#define STREAM_TIMEOUT 30000 typedef enum { AVDTP_ERROR_ERRNO, @@ -238,6 +239,7 @@ struct avdtp_stream { the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ + guint idle_timer; }; /* Structure describing an AVDTP connection between two devices */ @@ -471,6 +473,17 @@ static void stream_free(struct avdtp_stream *stream) g_free(stream); } +static gboolean stream_timeout(struct avdtp_stream *stream) +{ + struct avdtp *session = stream->session; + + avdtp_close(session, stream); + + stream->idle_timer = 0; + + return FALSE; +} + static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state) @@ -502,11 +515,32 @@ static void avdtp_sep_set_state(struct avdtp *session, } } - if (state == AVDTP_STATE_IDLE) { + switch (state) { + case AVDTP_STATE_OPEN: + stream->idle_timer = g_timeout_add(STREAM_TIMEOUT, + (GSourceFunc) stream_timeout, + stream); + break; + case AVDTP_STATE_STREAMING: + case AVDTP_STATE_CLOSING: + case AVDTP_STATE_ABORTING: + if (stream->idle_timer) { + g_source_remove(stream->idle_timer); + stream->idle_timer = 0; + } + break; + case AVDTP_STATE_IDLE: + if (stream->idle_timer) { + g_source_remove(stream->idle_timer); + stream->idle_timer = 0; + } session->streams = g_slist_remove(session->streams, stream); stream_free(stream); if (session->ref == 1 && !session->streams) set_disconnect_timer(session); + break; + default: + break; } } -- cgit From fa6a550673f30e6da10c0c4b62d5fedc1ebac787 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 18 Aug 2007 22:55:40 +0000 Subject: Reduce stream idle timeout from 30s to 20s --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 42a59aa5..993bf89c 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -71,7 +71,7 @@ #define REQ_TIMEOUT 2000 #define DISCONNECT_TIMEOUT 5000 -#define STREAM_TIMEOUT 30000 +#define STREAM_TIMEOUT 20000 typedef enum { AVDTP_ERROR_ERRNO, -- cgit From 78a94059160d7478624cad315022255f7ebbbe80 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 19 Aug 2007 15:27:56 +0000 Subject: Improve handling of unix clients which stay connected even though the headset connection is lost --- audio/a2dp.c | 3 ++- audio/avdtp.c | 7 ------- audio/unix.c | 5 +++++ 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index ce50c9cf..ebf6b472 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -118,10 +118,11 @@ static void setup_callback(struct a2dp_stream_cb *cb, cb->cb(s->session, s->dev, s->stream, cb->user_data); } -static void finalize_stream_setup(struct a2dp_stream_setup *s) +static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) { g_slist_foreach(setup->cb, (GFunc) setup_callback, setup); stream_setup_free(setup); + return FALSE; } static gboolean setconf_ind(struct avdtp *session, diff --git a/audio/avdtp.c b/audio/avdtp.c index 993bf89c..a82f21d5 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1518,7 +1518,6 @@ static gboolean request_timeout(gpointer user_data) struct avdtp *session = user_data; struct pending_req *req; struct seid_req sreq; - struct avdtp_remote_sep *rsep; struct avdtp_local_sep *lsep; struct avdtp_stream *stream; uint8_t seid; @@ -1531,12 +1530,6 @@ static gboolean request_timeout(gpointer user_data) seid = ((struct seid_req *) (req->msg))->acp_seid; - rsep = find_remote_sep(session->seps, seid); - if (!rsep) { - error("Unable to find matching remote SEID %u", seid); - goto failed; - } - stream = find_stream_by_rseid(session, seid); if (stream) diff --git a/audio/unix.c b/audio/unix.c index 94c84500..f8832c91 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -164,6 +164,8 @@ static void stream_state_changed(struct avdtp_stream *stream, switch (new_state) { case AVDTP_STATE_IDLE: + a2dp_source_unlock(client->dev, a2dp->session); + client->dev = NULL; if (a2dp->session) { avdtp_unref(a2dp->session); a2dp->session = NULL; @@ -315,6 +317,7 @@ proceed: client); if (id == 0) { error("request_stream failed"); + a2dp_source_unlock(dev, a2dp->session); goto failed; } @@ -395,6 +398,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) if (cond & (G_IO_HUP | G_IO_ERR)) { debug("Unix client disconnected"); + if (!client->dev) + goto failed; if (client->disconnect) client->disconnect(client->dev, cb_data); if (client->type == TYPE_SINK && client->req_id > 0) -- cgit From 730a4ceb9e7e986e2a327fa7e023c3dd2102a50d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Aug 2007 08:50:22 +0000 Subject: Improve handling of simultaneous connect attempts to different devices --- audio/a2dp.c | 24 ++++++++++++++++++++++-- audio/unix.c | 14 +++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index ebf6b472..0e5f1473 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -884,6 +884,17 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, struct a2dp_stream_cb *cb_data; static unsigned int cb_id = 0; + if (source.used_by != NULL && source.used_by != dev) { + error("a2dp_source_request_stream: SEP is locked"); + return 0; + } + + if (setup && setup->dev != dev) { + error("a2dp_source_request_stream: stream setup in progress " + "already for another device"); + return 0; + } + cb_data = g_new(struct a2dp_stream_cb, 1); cb_data->cb = cb; cb_data->user_data = user_data; @@ -906,8 +917,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, switch (avdtp_sep_get_state(source.sep)) { case AVDTP_STATE_IDLE: - if (avdtp_discover(session, discovery_complete, setup) < 0) + if (avdtp_discover(session, discovery_complete, setup) < 0) { + error("avdtp_discover failed"); goto failed; + } break; case AVDTP_STATE_OPEN: if (!start) { @@ -916,8 +929,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, } if (source.starting) break; - if (avdtp_start(session, source.stream) < 0) + if (avdtp_start(session, source.stream) < 0) { + error("avdtp_start failed"); goto failed; + } break; case AVDTP_STATE_STREAMING: if (!start || !source.suspending) { @@ -931,6 +946,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, source.start_requested = TRUE; break; default: + error("SEP in bad state for requesting a new stream"); goto failed; } @@ -947,6 +963,8 @@ gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) if (source.used_by) return FALSE; + debug("SBC Source locked"); + source.used_by = dev; return TRUE; @@ -966,6 +984,8 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) source.used_by = NULL; + debug("SBC Source unlocked"); + if (!source.stream || state == AVDTP_STATE_IDLE) return TRUE; diff --git a/audio/unix.c b/audio/unix.c index f8832c91..9bf1a9ca 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -198,6 +198,11 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, if (!stream) goto failed; + if (!a2dp_source_lock(dev, session)) { + error("Unable to lock A2DP source SEP"); + goto failed; + } + a2dp->stream = stream; if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { @@ -273,8 +278,9 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, failed: error("stream setup failed"); + if (a2dp->stream) + a2dp_source_unlock(dev, session); unix_send_cfg(client->sock, NULL, -1); - a2dp_source_unlock(dev, session); avdtp_unref(a2dp->session); a2dp->session = NULL; a2dp->stream = NULL; @@ -307,17 +313,11 @@ proceed: if (!a2dp->session) a2dp->session = avdtp_get(&dev->src, &dev->dst); - if (!a2dp_source_lock(dev, a2dp->session)) { - error("Unable to lock A2DP source SEP"); - goto failed; - } - id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client); if (id == 0) { error("request_stream failed"); - a2dp_source_unlock(dev, a2dp->session); goto failed; } -- cgit From 3919df0168ddf293912231e5e6477bc6f0c24115 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Aug 2007 09:05:02 +0000 Subject: Fix handling of multiple unix clients --- audio/pcm_bluetooth.c | 411 ++++++++++++++++++++++++++++++++------------------ audio/unix.c | 7 +- 2 files changed, 268 insertions(+), 150 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index c75f8af3..a7c90661 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -40,7 +41,7 @@ #include "ipc.h" #include "sbc.h" -//#define ENABLE_DEBUG +/*#define ENABLE_DEBUG*/ #define BUFFER_SIZE 2048 @@ -58,6 +59,9 @@ #define SCO_RXBUFS 0x04 #endif +#define PERIOD_TIME_USECS(data) (1000000.0 * \ + ((data)->io.period_size) / \ + (data)->io.rate) struct rtp_header { uint8_t cc:4; uint8_t x:1; @@ -93,13 +97,13 @@ struct bluetooth_a2dp { uint16_t seq_num; /* */ int frame_count; /* */ - int bandwithcount; - struct timeval bandwithtimestamp; + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ }; struct bluetooth_data { snd_pcm_ioplug_t io; - snd_pcm_sframes_t hw_ptr; + volatile snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ int stream_fd; /* Audio stream filedescriptor */ int sock; /* Daemon unix socket */ @@ -113,9 +117,9 @@ void memcpy_changeendian(void *dst, const void *src, int size) int i; const uint16_t *ptrsrc = src; uint16_t *ptrdst = dst; - for (i = 0; i < size / 2; i++) { + + for (i = 0; i < size / 2; i++) *ptrdst++ = htons(*ptrsrc++); - } } static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -132,6 +136,85 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io) return 0; } +static void *a2dp_playback_hw_thread(void *param) +{ + struct bluetooth_data* data = (struct bluetooth_data *)param; + unsigned int num_period_elapsed = 0; + unsigned long long starttime; /* in usecs */ + struct timeval tv; + int ret; + + gettimeofday(&tv, 0); + starttime = tv.tv_sec * 1000000 + tv.tv_usec; + + for(;;) { + unsigned long long curtime; + unsigned int ntimes; + + gettimeofday(&tv, 0); + + /* How much time period_time has elapsed since the thread started ? */ + curtime = tv.tv_sec * 1000000 + tv.tv_usec; + ntimes = (1.0 * (curtime - starttime)) / PERIOD_TIME_USECS(data); + + if (ntimes > num_period_elapsed) { + char c = 'w'; + data->hw_ptr = (data->hw_ptr + + (ntimes - num_period_elapsed) + * data->io.period_size) + % data->io.buffer_size; + DBG("pointer = %ld", data->hw_ptr); + /* Notify user that hardware pointer has moved */ + ret = write(data->a2dp.pipefd[1], &c, 1); + assert(ret == 1); + num_period_elapsed = ntimes; + } + /* Period time is usually no shorter that 1 ms, + no need to sleep for a shorter amount of time */ + usleep(1000); + /* Offer opportunity to be canceled by main thread */ + pthread_testcancel(); + } +} +static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp_data = &data->a2dp; + int ret = 0; + + DBG("%p", io); + + assert(a2dp_data->hw_thread == 0); + ret = -pthread_create(&a2dp_data->hw_thread, 0, a2dp_playback_hw_thread, data); + + DBG(" - return %d", ret); + + return ret; +} + +static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp_data = &data->a2dp; + int ret = 0; + + DBG("%p", io); + + /* Beware - We can be called more than once */ + if (a2dp_data->hw_thread != 0) { + ret = -pthread_cancel(a2dp_data->hw_thread); + if (ret != 0) + goto done; + + ret = -pthread_join(a2dp_data->hw_thread, 0); + } + +done: + a2dp_data->hw_thread = 0; + DBG(" - return %d", ret); + return ret; +} + static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -154,6 +237,12 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->cfg.codec == CFG_CODEC_SBC) sbc_finish(&data->a2dp.sbc); + if(data->a2dp.pipefd[0] > 0) + close(data->a2dp.pipefd[0]); + + if(data->a2dp.pipefd[1] > 0) + close(data->a2dp.pipefd[1]); + free(data); } @@ -171,6 +260,7 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; + char c = 'w'; DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); @@ -184,7 +274,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - return 0; + /* a2dp : wake up any client polling at us */ + return write(data->a2dp.pipefd[1], &c, 1); } static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, @@ -234,11 +325,80 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, return 0; err = errno; + SNDERR("%s (%d)", strerror(err), err); return -err; } +static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, + struct pollfd *pfd, + unsigned int space) +{ + assert(io); + assert(space >= 1); + + pfd[0].fd = ((struct bluetooth_data *)io->private_data)->stream_fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + return 1; +} + +static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, + struct pollfd *pfds, unsigned int nfds, + unsigned short *revents) +{ + assert(pfds && nfds == 1 && revents); + + *revents = pfds[0].revents; + + return 0; +} + +static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, + struct pollfd *pfd, + unsigned int space) +{ + struct bluetooth_data *data = io->private_data; + + DBG(""); + + assert(io); + assert(space >= 1); + assert(data->a2dp.pipefd[0] != 0); + + pfd[0].fd = data->a2dp.pipefd[0]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + return 1; +} + +static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, + struct pollfd *pfds, + unsigned int nfds, + unsigned short *revents) +{ + static char buf[1]; + int ret = 0; + + DBG(""); + + assert(pfds); + assert(nfds == 1); + assert(revents); + assert(pfds[0].fd != 0); + + if (io->state != SND_PCM_STATE_PREPARED) + ret = read(pfds[0].fd, buf, 1); + + *revents = (pfds[0].revents & ~POLLIN) | POLLOUT; + + return 0; +} + + static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, @@ -364,19 +524,14 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, return ret; } -static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) +static int avdtp_write(struct bluetooth_data *data) { - int count = 0, written = 0, ret = 0; + int ret = 0; struct rtp_header *header; struct rtp_payload *payload; struct bluetooth_a2dp *a2dp = &data->a2dp; -#ifdef ENABLE_DEBUG - static struct timeval send_date = { 0, 0 }; - static struct timeval prev_date = { 0, 0 }; - struct timeval send_delay = { 0, 0 }; - struct timeval sendz_delay = { 0, 0 }; -#endif + DBG(""); header = (void *) a2dp->buffer; payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -389,98 +544,24 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - while (count++ < 10) { -#ifdef ENABLE_DEBUG - gettimeofday(&send_date, NULL); -#endif - ret = send(data->stream_fd, a2dp->buffer, a2dp->count, - nonblock ? MSG_DONTWAIT : 0); - if (ret < 0) { - ret = -errno; - if (errno == EAGAIN) - goto retry; - fprintf(stderr, "send: %s (%d)\n", strerror(errno), - errno); - goto done; - } - - written += ret; - -#ifdef ENABLE_DEBUG - if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { - long delay, real, theo, delta; - - delay = (long) (send_delay.tv_sec * 1000 + - send_delay.tv_usec / 1000), - real = (long) (sendz_delay.tv_sec * 1000 + - sendz_delay.tv_usec / 1000); - theo = (long) (((float) a2dp->nsamples) / - data->cfg.rate * 1000.0); - delta = (long) (sendz_delay.tv_sec * 1000 + - sendz_delay.tv_usec / 1000) - - (long) (((float) a2dp->nsamples) / - data->cfg.rate * 1000.0); - - timersub(&send_date, &prev_date, &send_delay); - timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); - - printf("send %d (cumul=%d) samples (delay=%ld ms," - " real=%ld ms, theo=%ld ms," - " delta=%ld ms).\n", a2dp->samples, - a2dp->nsamples, delay, real, theo, - delta); - } -#endif - if (written == a2dp->count) - break; - - a2dp->count -= written; + ret = send(data->stream_fd, a2dp->buffer, a2dp->count, + MSG_DONTWAIT); + if(ret == -1) + ret = -errno; -retry: - DBG("send (retry)."); - usleep(150000); - } - -#ifdef ENABLE_DEBUG - prev_date = send_date; -#endif - - if (written != a2dp->count) - printf("Wrote %d not %d bytes\n", written, a2dp->count); - -#ifdef ENABLE_DEBUG - else { - /* Measure bandwith usage */ - struct timeval now = { 0, 0 }; - struct timeval interval = { 0, 0 }; - - if(a2dp->bandwithtimestamp.tv_sec == 0) - gettimeofday(&a2dp->bandwithtimestamp, NULL); - - /* See if we must wait again */ - gettimeofday(&now, NULL); - timersub(&now, &a2dp->bandwithtimestamp, &interval); - if(interval.tv_sec > 0) - printf("Bandwith: %d (%d kbps)\n", a2dp->bandwithcount, - a2dp->bandwithcount / 128); - a2dp->bandwithtimestamp = now; - a2dp->bandwithcount = 0; - } - - a2dp->bandwithcount += written; + /* Kernel side l2cap socket layer makes sure either everything + is buffered for sending, or nothing is buffered. + This assertion is to remind people of this fact (and be noticed + the day that changes) + */ + assert(ret < 0 || ret == a2dp->count); -#endif - -done: /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; a2dp->samples = 0; a2dp->seq_num++; - if (written > 0) - return written; - return ret; } @@ -497,9 +578,35 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, uint8_t *buff; static int codesize = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," - "io->nonblock=%u", areas->step, areas->first, - offset, size, io->nonblock); + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu" + , areas->step, areas->first, offset, size); + DBG("hw_ptr = %lu, appl_ptr = %lu" + , io->hw_ptr, io->appl_ptr); + + if(io->hw_ptr > io->appl_ptr) { + ret = bluetooth_a2dp_playback_stop(io); + if(ret == 0) + ret = -EPIPE; + goto done; + } + + /* Check if we should autostart */ + if(io->state == SND_PCM_STATE_PREPARED) { + snd_pcm_sw_params_t *swparams; + snd_pcm_uframes_t threshold; + + snd_pcm_sw_params_malloc(&swparams); + if(!snd_pcm_sw_params_current(io->pcm, swparams) + && !snd_pcm_sw_params_get_start_threshold(swparams, + &threshold) ) { + if(io->appl_ptr >= threshold) { + ret = snd_pcm_start(io->pcm); + if(ret != 0) + goto done; + } + } + snd_pcm_sw_params_free(swparams); + } if (codesize == 0) { /* How much data can be encoded by sbc at a time? */ @@ -548,7 +655,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { - ret = avdtp_write(data, io->nonblock); + ret = avdtp_write(data); if (ret < 0) { if (-ret == EPIPE) ret = -EIO; @@ -561,55 +668,60 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, a2dp->frame_count++; a2dp->samples += encoded / frame_size; a2dp->nsamples += encoded / frame_size; - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + encoded / frame_size) - % io->buffer_size; ret = frames_to_read; done: - DBG("returning %lu", ret); + DBG("returning %ld", ret); return ret; } static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_write, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_write, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_read, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_read, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_write, + .start = bluetooth_a2dp_playback_start, + .stop = bluetooth_a2dp_playback_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_write, + .poll_descriptors = bluetooth_a2dp_playback_poll_descriptors, + .poll_revents = bluetooth_a2dp_playback_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_read, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_read, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -661,7 +773,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 200); + 2, 50); if (err < 0) return err; @@ -678,13 +790,13 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) .iov_len = sizeof(pkt) }; struct msghdr msgh = { - .msg_name = 0, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg_b, - .msg_controllen = CMSG_LEN(sizeof(int)), - .msg_flags = 0 + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &cmsg_b, + .msg_controllen = CMSG_LEN(sizeof(int)), + .msg_flags = 0 }; ret = recvmsg(data->sock, &msgh, 0); @@ -738,6 +850,14 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; + + if(pipe(a2dp->pipefd) != 0) + return -errno; + if(fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) + return -errno; + if(fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) + return -errno; + return 0; } @@ -888,7 +1008,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) DBG("Bluetooth PCM plugin (%s)", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - data = malloc(sizeof(struct bluetooth_data)); + data = calloc(1, sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; @@ -901,9 +1021,6 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; data->io.mmap_rw = 0; /* No direct mmap communication */ - data->io.poll_fd = data->stream_fd; - data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? - POLLOUT : POLLIN; data->io.private_data = data; if (data->cfg.codec == CFG_CODEC_SBC) diff --git a/audio/unix.c b/audio/unix.c index 9bf1a9ca..19a140c0 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -203,6 +203,10 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, goto failed; } + client->disconnect = (notify_cb_t) a2dp_source_unlock; + client->suspend = (notify_cb_t) a2dp_source_suspend; + client->play = (notify_cb_t) a2dp_source_start_stream; + a2dp->stream = stream; if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { @@ -322,9 +326,6 @@ proceed: } client->req_id = id; - client->disconnect = (notify_cb_t) a2dp_source_unlock; - client->suspend = (notify_cb_t) a2dp_source_suspend; - client->play = (notify_cb_t) a2dp_source_start_stream; break; case TYPE_HEADSET: -- cgit From 8905b2bad9aebd0911d452b53b9cbe975ad5bdcc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Aug 2007 09:06:13 +0000 Subject: Revert accidental commit of pcm_bluetooth.c changes --- audio/pcm_bluetooth.c | 411 ++++++++++++++++++-------------------------------- 1 file changed, 147 insertions(+), 264 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a7c90661..c75f8af3 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -28,7 +28,6 @@ #include #include #include -#include #include @@ -41,7 +40,7 @@ #include "ipc.h" #include "sbc.h" -/*#define ENABLE_DEBUG*/ +//#define ENABLE_DEBUG #define BUFFER_SIZE 2048 @@ -59,9 +58,6 @@ #define SCO_RXBUFS 0x04 #endif -#define PERIOD_TIME_USECS(data) (1000000.0 * \ - ((data)->io.period_size) / \ - (data)->io.rate) struct rtp_header { uint8_t cc:4; uint8_t x:1; @@ -97,13 +93,13 @@ struct bluetooth_a2dp { uint16_t seq_num; /* */ int frame_count; /* */ - pthread_t hw_thread; /* Makes virtual hw pointer move */ - int pipefd[2]; /* Inter thread communication */ + int bandwithcount; + struct timeval bandwithtimestamp; }; struct bluetooth_data { snd_pcm_ioplug_t io; - volatile snd_pcm_sframes_t hw_ptr; + snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ int stream_fd; /* Audio stream filedescriptor */ int sock; /* Daemon unix socket */ @@ -117,9 +113,9 @@ void memcpy_changeendian(void *dst, const void *src, int size) int i; const uint16_t *ptrsrc = src; uint16_t *ptrdst = dst; - - for (i = 0; i < size / 2; i++) + for (i = 0; i < size / 2; i++) { *ptrdst++ = htons(*ptrsrc++); + } } static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -136,85 +132,6 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io) return 0; } -static void *a2dp_playback_hw_thread(void *param) -{ - struct bluetooth_data* data = (struct bluetooth_data *)param; - unsigned int num_period_elapsed = 0; - unsigned long long starttime; /* in usecs */ - struct timeval tv; - int ret; - - gettimeofday(&tv, 0); - starttime = tv.tv_sec * 1000000 + tv.tv_usec; - - for(;;) { - unsigned long long curtime; - unsigned int ntimes; - - gettimeofday(&tv, 0); - - /* How much time period_time has elapsed since the thread started ? */ - curtime = tv.tv_sec * 1000000 + tv.tv_usec; - ntimes = (1.0 * (curtime - starttime)) / PERIOD_TIME_USECS(data); - - if (ntimes > num_period_elapsed) { - char c = 'w'; - data->hw_ptr = (data->hw_ptr + - (ntimes - num_period_elapsed) - * data->io.period_size) - % data->io.buffer_size; - DBG("pointer = %ld", data->hw_ptr); - /* Notify user that hardware pointer has moved */ - ret = write(data->a2dp.pipefd[1], &c, 1); - assert(ret == 1); - num_period_elapsed = ntimes; - } - /* Period time is usually no shorter that 1 ms, - no need to sleep for a shorter amount of time */ - usleep(1000); - /* Offer opportunity to be canceled by main thread */ - pthread_testcancel(); - } -} -static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp_data = &data->a2dp; - int ret = 0; - - DBG("%p", io); - - assert(a2dp_data->hw_thread == 0); - ret = -pthread_create(&a2dp_data->hw_thread, 0, a2dp_playback_hw_thread, data); - - DBG(" - return %d", ret); - - return ret; -} - -static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp_data = &data->a2dp; - int ret = 0; - - DBG("%p", io); - - /* Beware - We can be called more than once */ - if (a2dp_data->hw_thread != 0) { - ret = -pthread_cancel(a2dp_data->hw_thread); - if (ret != 0) - goto done; - - ret = -pthread_join(a2dp_data->hw_thread, 0); - } - -done: - a2dp_data->hw_thread = 0; - DBG(" - return %d", ret); - return ret; -} - static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -237,12 +154,6 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->cfg.codec == CFG_CODEC_SBC) sbc_finish(&data->a2dp.sbc); - if(data->a2dp.pipefd[0] > 0) - close(data->a2dp.pipefd[0]); - - if(data->a2dp.pipefd[1] > 0) - close(data->a2dp.pipefd[1]); - free(data); } @@ -260,7 +171,6 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - char c = 'w'; DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); @@ -274,8 +184,7 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - /* a2dp : wake up any client polling at us */ - return write(data->a2dp.pipefd[1], &c, 1); + return 0; } static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, @@ -325,80 +234,11 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, return 0; err = errno; - SNDERR("%s (%d)", strerror(err), err); return -err; } -static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, - unsigned int space) -{ - assert(io); - assert(space >= 1); - - pfd[0].fd = ((struct bluetooth_data *)io->private_data)->stream_fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - - return 1; -} - -static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, - struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) -{ - assert(pfds && nfds == 1 && revents); - - *revents = pfds[0].revents; - - return 0; -} - -static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, - unsigned int space) -{ - struct bluetooth_data *data = io->private_data; - - DBG(""); - - assert(io); - assert(space >= 1); - assert(data->a2dp.pipefd[0] != 0); - - pfd[0].fd = data->a2dp.pipefd[0]; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - - return 1; -} - -static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, - struct pollfd *pfds, - unsigned int nfds, - unsigned short *revents) -{ - static char buf[1]; - int ret = 0; - - DBG(""); - - assert(pfds); - assert(nfds == 1); - assert(revents); - assert(pfds[0].fd != 0); - - if (io->state != SND_PCM_STATE_PREPARED) - ret = read(pfds[0].fd, buf, 1); - - *revents = (pfds[0].revents & ~POLLIN) | POLLOUT; - - return 0; -} - - static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, @@ -524,14 +364,19 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, return ret; } -static int avdtp_write(struct bluetooth_data *data) +static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) { - int ret = 0; + int count = 0, written = 0, ret = 0; struct rtp_header *header; struct rtp_payload *payload; struct bluetooth_a2dp *a2dp = &data->a2dp; +#ifdef ENABLE_DEBUG + static struct timeval send_date = { 0, 0 }; + static struct timeval prev_date = { 0, 0 }; + struct timeval send_delay = { 0, 0 }; + struct timeval sendz_delay = { 0, 0 }; +#endif - DBG(""); header = (void *) a2dp->buffer; payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -544,24 +389,98 @@ static int avdtp_write(struct bluetooth_data *data) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - ret = send(data->stream_fd, a2dp->buffer, a2dp->count, - MSG_DONTWAIT); - if(ret == -1) - ret = -errno; + while (count++ < 10) { +#ifdef ENABLE_DEBUG + gettimeofday(&send_date, NULL); +#endif + ret = send(data->stream_fd, a2dp->buffer, a2dp->count, + nonblock ? MSG_DONTWAIT : 0); + if (ret < 0) { + ret = -errno; + if (errno == EAGAIN) + goto retry; + fprintf(stderr, "send: %s (%d)\n", strerror(errno), + errno); + goto done; + } + + written += ret; + +#ifdef ENABLE_DEBUG + if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { + long delay, real, theo, delta; + + delay = (long) (send_delay.tv_sec * 1000 + + send_delay.tv_usec / 1000), + real = (long) (sendz_delay.tv_sec * 1000 + + sendz_delay.tv_usec / 1000); + theo = (long) (((float) a2dp->nsamples) / + data->cfg.rate * 1000.0); + delta = (long) (sendz_delay.tv_sec * 1000 + + sendz_delay.tv_usec / 1000) - + (long) (((float) a2dp->nsamples) / + data->cfg.rate * 1000.0); + + timersub(&send_date, &prev_date, &send_delay); + timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); + + printf("send %d (cumul=%d) samples (delay=%ld ms," + " real=%ld ms, theo=%ld ms," + " delta=%ld ms).\n", a2dp->samples, + a2dp->nsamples, delay, real, theo, + delta); + } +#endif + if (written == a2dp->count) + break; + + a2dp->count -= written; - /* Kernel side l2cap socket layer makes sure either everything - is buffered for sending, or nothing is buffered. - This assertion is to remind people of this fact (and be noticed - the day that changes) - */ - assert(ret < 0 || ret == a2dp->count); +retry: + DBG("send (retry)."); + usleep(150000); + } + +#ifdef ENABLE_DEBUG + prev_date = send_date; +#endif + + if (written != a2dp->count) + printf("Wrote %d not %d bytes\n", written, a2dp->count); + +#ifdef ENABLE_DEBUG + else { + /* Measure bandwith usage */ + struct timeval now = { 0, 0 }; + struct timeval interval = { 0, 0 }; + + if(a2dp->bandwithtimestamp.tv_sec == 0) + gettimeofday(&a2dp->bandwithtimestamp, NULL); + + /* See if we must wait again */ + gettimeofday(&now, NULL); + timersub(&now, &a2dp->bandwithtimestamp, &interval); + if(interval.tv_sec > 0) + printf("Bandwith: %d (%d kbps)\n", a2dp->bandwithcount, + a2dp->bandwithcount / 128); + a2dp->bandwithtimestamp = now; + a2dp->bandwithcount = 0; + } + + a2dp->bandwithcount += written; +#endif + +done: /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; a2dp->samples = 0; a2dp->seq_num++; + if (written > 0) + return written; + return ret; } @@ -578,35 +497,9 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, uint8_t *buff; static int codesize = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu" - , areas->step, areas->first, offset, size); - DBG("hw_ptr = %lu, appl_ptr = %lu" - , io->hw_ptr, io->appl_ptr); - - if(io->hw_ptr > io->appl_ptr) { - ret = bluetooth_a2dp_playback_stop(io); - if(ret == 0) - ret = -EPIPE; - goto done; - } - - /* Check if we should autostart */ - if(io->state == SND_PCM_STATE_PREPARED) { - snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t threshold; - - snd_pcm_sw_params_malloc(&swparams); - if(!snd_pcm_sw_params_current(io->pcm, swparams) - && !snd_pcm_sw_params_get_start_threshold(swparams, - &threshold) ) { - if(io->appl_ptr >= threshold) { - ret = snd_pcm_start(io->pcm); - if(ret != 0) - goto done; - } - } - snd_pcm_sw_params_free(swparams); - } + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," + "io->nonblock=%u", areas->step, areas->first, + offset, size, io->nonblock); if (codesize == 0) { /* How much data can be encoded by sbc at a time? */ @@ -655,7 +548,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { - ret = avdtp_write(data); + ret = avdtp_write(data, io->nonblock); if (ret < 0) { if (-ret == EPIPE) ret = -EIO; @@ -668,60 +561,55 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, a2dp->frame_count++; a2dp->samples += encoded / frame_size; a2dp->nsamples += encoded / frame_size; + /* Increment hardware transmition pointer */ + data->hw_ptr = (data->hw_ptr + encoded / frame_size) + % io->buffer_size; ret = frames_to_read; done: - DBG("returning %ld", ret); + DBG("returning %lu", ret); return ret; } static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_write, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_write, }; static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_read, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_read, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { - .start = bluetooth_a2dp_playback_start, - .stop = bluetooth_a2dp_playback_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_write, - .poll_descriptors = bluetooth_a2dp_playback_poll_descriptors, - .poll_revents = bluetooth_a2dp_playback_poll_revents, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_write, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_read, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_read, }; #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -773,7 +661,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 50); + 2, 200); if (err < 0) return err; @@ -790,13 +678,13 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) .iov_len = sizeof(pkt) }; struct msghdr msgh = { - .msg_name = 0, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg_b, - .msg_controllen = CMSG_LEN(sizeof(int)), - .msg_flags = 0 + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &cmsg_b, + .msg_controllen = CMSG_LEN(sizeof(int)), + .msg_flags = 0 }; ret = recvmsg(data->sock, &msgh, 0); @@ -850,14 +738,6 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; - - if(pipe(a2dp->pipefd) != 0) - return -errno; - if(fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) - return -errno; - if(fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) - return -errno; - return 0; } @@ -1008,7 +888,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) DBG("Bluetooth PCM plugin (%s)", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - data = calloc(1, sizeof(struct bluetooth_data)); + data = malloc(sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; @@ -1021,6 +901,9 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; data->io.mmap_rw = 0; /* No direct mmap communication */ + data->io.poll_fd = data->stream_fd; + data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? + POLLOUT : POLLIN; data->io.private_data = data; if (data->cfg.codec == CFG_CODEC_SBC) -- cgit From 7a29d3596b6680c421aa28051ec6baeea1bbfa6b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 20 Aug 2007 15:12:33 +0000 Subject: Improve robustness of A2DP SEP active stream state by using a stream state callback instead of the SEP callbacks --- audio/a2dp.c | 59 ++++++++++++++++++++++++++++++++--------------------------- audio/avdtp.c | 4 +++- 2 files changed, 35 insertions(+), 28 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 0e5f1473..1e22e7c8 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -87,21 +87,6 @@ static struct a2dp_sep source = { NULL }; static struct a2dp_stream_setup *setup = NULL; -static void stream_cleanup(struct a2dp_sep *sep) -{ - if (sep->suspend_timer) { - g_source_remove(sep->suspend_timer); - sep->suspend_timer = 0; - } - - if (sep->session) { - avdtp_unref(sep->session); - sep->session = NULL; - } - - sep->stream = NULL; -} - static void stream_setup_free(struct a2dp_stream_setup *s) { if (s->session) @@ -125,6 +110,30 @@ static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) return FALSE; } +static void stream_state_changed(struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data) +{ + struct a2dp_sep *sep = user_data; + + if (new_state != AVDTP_STATE_IDLE) + return; + + if (sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; + } + + if (sep->session) { + avdtp_unref(sep->session); + sep->session = NULL; + } + + sep->stream = NULL; +} + static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, @@ -152,6 +161,7 @@ static gboolean setconf_ind(struct avdtp *session, return FALSE; } + avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; if (a2dp_sep == &source) @@ -230,12 +240,12 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } if (err) { - a2dp_sep->stream = NULL; if (setup) finalize_stream_setup(setup); return; } + avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; if (!setup) @@ -245,7 +255,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (ret < 0) { error("Error on avdtp_open %s (%d)", strerror(-ret), -ret); - setup->stream = FALSE; + setup->stream = NULL; finalize_stream_setup(setup); } } @@ -319,7 +329,7 @@ static gboolean suspend_timeout(struct a2dp_sep *sep) if (avdtp_suspend(sep->session, sep->stream) == 0) sep->suspending = TRUE; - sep->suspend_timer = FALSE; + sep->suspend_timer = 0; avdtp_unref(sep->session); sep->session = NULL; @@ -346,7 +356,6 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, (GSourceFunc) suspend_timeout, a2dp_sep); - return TRUE; } @@ -425,8 +434,6 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, a2dp_sep = &source; } - stream_cleanup(a2dp_sep); - return TRUE; } @@ -442,8 +449,6 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Source: Close_Cfm"); a2dp_sep = &source; } - - stream_cleanup(a2dp_sep); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -459,8 +464,6 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, a2dp_sep = &source; } - stream_cleanup(a2dp_sep); - return TRUE; } @@ -476,8 +479,6 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Source: Abort_Cfm"); a2dp_sep = &source; } - - stream_cleanup(a2dp_sep); } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -940,6 +941,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, g_source_remove(source.suspend_timer); source.suspend_timer = 0; } + if (source.session) { + avdtp_unref(source.session); + source.session = NULL; + } g_idle_add((GSourceFunc) finalize_stream_setup, setup); return cb_data->id; } diff --git a/audio/avdtp.c b/audio/avdtp.c index a82f21d5..57de80b9 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -326,8 +326,10 @@ static gboolean avdtp_send(struct avdtp *session, void *data, int len) { int ret; - if (session->sock < 0) + if (session->sock < 0) { + error("avdtp_send: session is closed"); return FALSE; + } ret = send(session->sock, data, len, 0); -- cgit From f4f92d015b2bf4b5478d62fb8f4d0e28443f25cf Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 20 Aug 2007 16:57:03 +0000 Subject: Fix load of default device from storage. --- audio/manager.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 20b91435..6eebce94 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1113,8 +1113,9 @@ static void register_devices_stored(const char *adapter) bacpy(&default_src, BDADDR_ANY); dev_id = hci_get_route(&default_src); if (dev_id < 0) - hci_devba(dev_id, &default_src); - + return; + + hci_devba(dev_id, &default_src); if (bacmp(&default_src, &src) != 0) return; -- cgit From c92c420f83dda53ebde74ad72cd0a3959d430901 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 06:48:14 +0000 Subject: Implement RequestAuthorization support --- audio/a2dp.c | 4 +- audio/avdtp.c | 89 ++++++++++++++++++++++++++++++++++------ audio/manager.c | 125 +++++++++++++++++++++++++++++++++----------------------- audio/manager.h | 7 ++++ audio/sink.c | 4 ++ audio/unix.c | 5 +++ 6 files changed, 170 insertions(+), 64 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 1e22e7c8..febc112c 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -607,8 +607,8 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source if (enable_sink) { source.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); + AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm); if (source.sep == NULL) return -1; diff --git a/audio/avdtp.c b/audio/avdtp.c index 57de80b9..be2709da 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -36,10 +36,13 @@ #include #include +#include #include "dbus.h" #include "logging.h" +#include "device.h" +#include "manager.h" #include "avdtp.h" #include @@ -274,6 +277,8 @@ struct avdtp { struct pending_req *req; guint dc_timer; + + DBusPendingCall *pending_auth; }; struct avdtp_error { @@ -1363,7 +1368,15 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: + if (session->pending_auth) { + manager_cancel_authorize(&session->dst, ADVANCED_AUDIO_UUID, + session->pending_auth); + dbus_pending_call_unref(session->pending_auth); + session->pending_auth = NULL; + } + connection_lost(session, -EIO); + return FALSE; } @@ -2041,8 +2054,12 @@ static struct avdtp *avdtp_get_internal(bdaddr_t *src, bdaddr_t *dst) assert(dst != NULL); session = find_session(src, dst); - if (session) - return session; + if (session) { + if (session->pending_auth) + return NULL; + else + return session; + } session = g_new0(struct avdtp, 1); @@ -2063,6 +2080,9 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) session = avdtp_get_internal(src, dst); + if (!session) + return NULL; + return avdtp_ref(session); } @@ -2463,6 +2483,51 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep) return 0; } +static void auth_cb(DBusPendingCall *call, void *data) +{ + GIOChannel *io; + struct avdtp *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); + } + + dbus_error_free(&err); + + connection_lost(session, -EACCES); + + dbus_message_unref(reply); + + return; + } + + session->buf = g_malloc0(session->mtu); + + set_disconnect_timer(session); + + session->state = AVDTP_SESSION_STATE_CONNECTED; + + 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 avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; @@ -2471,7 +2536,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) struct l2cap_options l2o; bdaddr_t src, dst; struct avdtp *session; - GIOChannel *cli_io; + GIOChannel *io; char address[18]; if (cond & G_IO_NVAL) @@ -2529,19 +2594,19 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (session->ref == 1) - set_disconnect_timer(session); + if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb, session, + &session->pending_auth)) { + close(cli_sk); + return TRUE; + } session->mtu = l2o.imtu; - session->buf = g_malloc0(session->mtu); session->sock = cli_sk; - session->state = AVDTP_SESSION_STATE_CONNECTED; - cli_io = g_io_channel_unix_new(session->sock); - session->io = g_io_add_watch(cli_io, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) session_cb, session); - g_io_channel_unref(cli_io); + io = g_io_channel_unix_new(session->sock); + session->io = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) session_cb, session); + g_io_channel_unref(io); return TRUE; } diff --git a/audio/manager.c b/audio/manager.c index 6eebce94..4d8d424d 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1383,10 +1383,11 @@ int remove_service_record(DBusConnection *conn, uint32_t rec_id) return 0; } -static void send_cancel_auth(struct device *device) +static void auth_cb(DBusPendingCall *call, void *data) { - DBusMessage *cancel; - char addr[18], *address = addr; + struct device *device = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; const char *uuid; if (headset_get_type(device) == SVC_HEADSET) @@ -1394,35 +1395,12 @@ static void send_cancel_auth(struct device *device) else uuid = HFP_AG_UUID; - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(&device->dst, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - send_message_and_unref(connection, cancel); -} - -static void auth_cb(DBusPendingCall *call, void *data) -{ - struct device *device = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - 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"); - send_cancel_auth(device); + manager_cancel_authorize(&device->dst, uuid, NULL); } dbus_error_free(&err); @@ -1446,11 +1424,8 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) int srv_sk, cli_sk; struct sockaddr_rc addr; socklen_t size; - char hs_address[18], *address = hs_address; const char *uuid; struct device *device; - DBusMessage *auth; - DBusPendingCall *pending; headset_type_t type; if (cond & G_IO_NVAL) @@ -1500,28 +1475,9 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) headset_set_type(device, type); - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); + if (!manager_authorize(&device->dst, uuid, auth_cb, device, NULL)) goto failed; - } - ba2str(&device->dst, hs_address); - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, auth, &pending, -1)) { - error("Sending of authorization request failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, auth_cb, device, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(auth); headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); return TRUE; @@ -1734,3 +1690,72 @@ struct device *manager_get_connected_device(void) return NULL; } + +void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, + DBusPendingCall *pending) +{ + DBusMessage *cancel; + char addr[18], *address = addr; + + if (pending) + dbus_pending_call_cancel(pending); + + cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "CancelAuthorizationRequest"); + if (!cancel) { + error("Unable to allocate new method call"); + return; + } + + ba2str(dba, addr); + + dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + send_message_and_unref(connection, cancel); +} + +gboolean manager_authorize(bdaddr_t *dba, const char *uuid, + DBusPendingCallNotifyFunction cb, + void *user_data, + DBusPendingCall **pending) +{ + DBusMessage *auth; + char address[18], *addr_ptr = address; + DBusPendingCall *p; + + ba2str(dba, address); + + debug("Requesting authorization for device %s, UUID %s", + address, uuid); + + auth = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.Database", + "RequestAuthorization"); + if (!auth) { + error("Unable to allocate RequestAuthorization method call"); + return FALSE; + } + + dbus_message_append_args(auth, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (!dbus_connection_send_with_reply(connection, auth, &p, -1)) { + error("Sending of authorization request failed"); + dbus_message_unref(auth); + return FALSE; + } + + dbus_pending_call_set_notify(p, cb, user_data, NULL); + if (pending) + *pending = p; + else + dbus_pending_call_unref(p); + + dbus_message_unref(auth); + + return TRUE; +} diff --git a/audio/manager.h b/audio/manager.h index a18cf4fd..65992040 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -47,3 +47,10 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); struct device *manager_default_device(); struct device *manager_get_connected_device(void); + +gboolean manager_authorize(bdaddr_t *dba, const char *uuid, + DBusPendingCallNotifyFunction cb, + void *user_data, + DBusPendingCall **pending); +void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, + DBusPendingCall *pending); diff --git a/audio/sink.c b/audio/sink.c index ab9f401b..c09c019f 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -160,6 +160,10 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); + if (!sink->session) + return err_connect_failed(conn, msg, + "Unable to get a session"); + if (sink->connect || sink->disconnect) return err_connect_failed(conn, msg, "Connect in progress"); diff --git a/audio/unix.c b/audio/unix.c index 19a140c0..3488fd5f 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -317,6 +317,11 @@ proceed: if (!a2dp->session) a2dp->session = avdtp_get(&dev->src, &dev->dst); + if (!a2dp->session) { + error("Unable to get a session"); + goto failed; + } + id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client); -- cgit From cfa764640b52854ffa77e962996d716876fdd115 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 07:23:16 +0000 Subject: Plug a memory leak when manager_authorize fails --- audio/avdtp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index be2709da..649e69a4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2597,6 +2597,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb, session, &session->pending_auth)) { close(cli_sk); + avdtp_unref(session); return TRUE; } -- cgit From 053db67fe83c502f5e509522497bed6fb721d4c7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 08:02:07 +0000 Subject: Handle connect attempts during authorization better --- audio/avdtp.c | 12 +++++++++++- audio/manager.c | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 649e69a4..0b56340f 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2088,7 +2088,17 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst) { - return find_session(src, dst) == NULL ? FALSE : TRUE; + struct avdtp *session; + + session = find_session(src, dst); + + if (!session) + return FALSE; + + if (session->state != AVDTP_SESSION_STATE_DISCONNECTED) + return TRUE; + + return FALSE; } gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, diff --git a/audio/manager.c b/audio/manager.c index 4d8d424d..c1a72b8f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1681,7 +1681,8 @@ struct device *manager_get_connected_device(void) for (l = devices; l != NULL; l = g_slist_next(l)) { struct device *device = l->data; - if (device->sink && sink_is_active(device)) + if ((device->sink || device->source) && + avdtp_is_connected(&device->src, &device->dst)) return device; if (device->headset && headset_is_active(device)) -- cgit From e7407648f54c18f4995eff0dc4c809b33a306fcd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:01:45 +0000 Subject: Implement proper timing for sending AVDTP stream --- audio/pcm_bluetooth.c | 425 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 278 insertions(+), 147 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index c75f8af3..cae080f2 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -40,7 +41,7 @@ #include "ipc.h" #include "sbc.h" -//#define ENABLE_DEBUG +/*#define ENABLE_DEBUG*/ #define BUFFER_SIZE 2048 @@ -93,13 +94,13 @@ struct bluetooth_a2dp { uint16_t seq_num; /* */ int frame_count; /* */ - int bandwithcount; - struct timeval bandwithtimestamp; + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ }; struct bluetooth_data { snd_pcm_ioplug_t io; - snd_pcm_sframes_t hw_ptr; + volatile snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ int stream_fd; /* Audio stream filedescriptor */ int sock; /* Daemon unix socket */ @@ -113,9 +114,9 @@ void memcpy_changeendian(void *dst, const void *src, int size) int i; const uint16_t *ptrsrc = src; uint16_t *ptrdst = dst; - for (i = 0; i < size / 2; i++) { + + for (i = 0; i < size / 2; i++) *ptrdst++ = htons(*ptrsrc++); - } } static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -132,6 +133,97 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io) return 0; } +static void *a2dp_playback_hw_thread(void *param) +{ + struct bluetooth_data *data = param; + unsigned int prev_periods; + double period_time; + struct timeval start; + + prev_periods = 0; + period_time = 1000000.0 * data->io.period_size / data->io.rate; + + gettimeofday(&start, 0); + + while (1) { + unsigned long long dtime; + unsigned int periods; + struct timeval cur, delta; + + gettimeofday(&cur, 0); + + timersub(&cur, &start, &delta); + + dtime = delta.tv_sec * 1000000 + delta.tv_usec; + periods = 1.0 * dtime / period_time; + + if (periods > prev_periods) { + char c = 'w'; + + data->hw_ptr += (periods - prev_periods) * + data->io.period_size; + data->hw_ptr %= data->io.buffer_size; + + DBG("pointer = %ld", data->hw_ptr); + + /* Notify user that hardware pointer has moved */ + if (write(data->a2dp.pipefd[1], &c, 1) < 0) + pthread_testcancel(); + + prev_periods = periods; + } + + usleep(period_time); + + /* Offer opportunity to be canceled by main thread */ + pthread_testcancel(); + } +} +static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp_data = &data->a2dp; + int err; + + DBG("%p", io); + + assert(a2dp_data->hw_thread == 0); + + err = pthread_create(&a2dp_data->hw_thread, 0, + a2dp_playback_hw_thread, data); + + DBG(" - return %d", -err); + + return -err; +} + +static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp_data = &data->a2dp; + int err; + + DBG("%p", io); + + /* Beware - We can be called more than once */ + if (a2dp_data->hw_thread == 0) + return 0; + + err = pthread_cancel(a2dp_data->hw_thread); + if (err != 0) + goto failed; + + err = pthread_join(a2dp_data->hw_thread, 0); + if (err != 0) + goto failed; + + a2dp_data->hw_thread = 0; + +failed: + DBG(" - return %d", -err); + return -err; +} + static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -154,6 +246,12 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->cfg.codec == CFG_CODEC_SBC) sbc_finish(&data->a2dp.sbc); + if(data->a2dp.pipefd[0] > 0) + close(data->a2dp.pipefd[0]); + + if(data->a2dp.pipefd[1] > 0) + close(data->a2dp.pipefd[1]); + free(data); } @@ -171,6 +269,7 @@ static int bluetooth_close(snd_pcm_ioplug_t *io) static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; + char c = 'w'; DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", io->period_size, io->buffer_size); @@ -184,7 +283,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - return 0; + /* a2dp : wake up any client polling at us */ + return write(data->a2dp.pipefd[1], &c, 1); } static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, @@ -234,11 +334,85 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, return 0; err = errno; + SNDERR("%s (%d)", strerror(err), err); return -err; } +static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, + struct pollfd *pfd, + unsigned int space) +{ + struct bluetooth_data *data = io->private_data; + + assert(io); + + if (space < 1) + return 0; + + pfd[0].fd = data->stream_fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + return 1; +} + +static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, + struct pollfd *pfds, unsigned int nfds, + unsigned short *revents) +{ + assert(pfds && nfds == 1 && revents); + + *revents = pfds[0].revents; + + return 0; +} + +static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, + struct pollfd *pfd, + unsigned int space) +{ + struct bluetooth_data *data = io->private_data; + + DBG(""); + + assert(data->a2dp.pipefd[0] >= 0); + + if (space < 1) + return 0; + + pfd[0].fd = data->a2dp.pipefd[0]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + return 1; +} + +static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, + struct pollfd *pfds, + unsigned int nfds, + unsigned short *revents) +{ + static char buf[1]; + int ret; + + DBG(""); + + assert(pfds); + assert(nfds == 1); + assert(revents); + assert(pfds[0].fd >= 0); + + if (io->state != SND_PCM_STATE_PREPARED) + ret = read(pfds[0].fd, buf, 1); + + *revents = (pfds[0].revents & ~POLLIN) | POLLOUT; + + return 0; +} + + static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, @@ -364,19 +538,14 @@ static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, return ret; } -static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) +static int avdtp_write(struct bluetooth_data *data) { - int count = 0, written = 0, ret = 0; + int ret = 0; struct rtp_header *header; struct rtp_payload *payload; struct bluetooth_a2dp *a2dp = &data->a2dp; -#ifdef ENABLE_DEBUG - static struct timeval send_date = { 0, 0 }; - static struct timeval prev_date = { 0, 0 }; - struct timeval send_delay = { 0, 0 }; - struct timeval sendz_delay = { 0, 0 }; -#endif + DBG(""); header = (void *) a2dp->buffer; payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -389,98 +558,24 @@ static int avdtp_write(struct bluetooth_data *data, unsigned int nonblock) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - while (count++ < 10) { -#ifdef ENABLE_DEBUG - gettimeofday(&send_date, NULL); -#endif - ret = send(data->stream_fd, a2dp->buffer, a2dp->count, - nonblock ? MSG_DONTWAIT : 0); - if (ret < 0) { - ret = -errno; - if (errno == EAGAIN) - goto retry; - fprintf(stderr, "send: %s (%d)\n", strerror(errno), - errno); - goto done; - } + ret = send(data->stream_fd, a2dp->buffer, a2dp->count, + MSG_DONTWAIT); + if(ret == -1) + ret = -errno; - written += ret; + /* Kernel side l2cap socket layer makes sure either everything + is buffered for sending, or nothing is buffered. + This assertion is to remind people of this fact (and be noticed + the day that changes) + */ + assert(ret < 0 || ret == a2dp->count); -#ifdef ENABLE_DEBUG - if ((written >= 0 || errno == EAGAIN) && prev_date.tv_sec != 0) { - long delay, real, theo, delta; - - delay = (long) (send_delay.tv_sec * 1000 + - send_delay.tv_usec / 1000), - real = (long) (sendz_delay.tv_sec * 1000 + - sendz_delay.tv_usec / 1000); - theo = (long) (((float) a2dp->nsamples) / - data->cfg.rate * 1000.0); - delta = (long) (sendz_delay.tv_sec * 1000 + - sendz_delay.tv_usec / 1000) - - (long) (((float) a2dp->nsamples) / - data->cfg.rate * 1000.0); - - timersub(&send_date, &prev_date, &send_delay); - timersub(&send_date, &a2dp->ntimestamp, &sendz_delay); - - printf("send %d (cumul=%d) samples (delay=%ld ms," - " real=%ld ms, theo=%ld ms," - " delta=%ld ms).\n", a2dp->samples, - a2dp->nsamples, delay, real, theo, - delta); - } -#endif - if (written == a2dp->count) - break; - - a2dp->count -= written; - -retry: - DBG("send (retry)."); - usleep(150000); - } - -#ifdef ENABLE_DEBUG - prev_date = send_date; -#endif - - if (written != a2dp->count) - printf("Wrote %d not %d bytes\n", written, a2dp->count); - -#ifdef ENABLE_DEBUG - else { - /* Measure bandwith usage */ - struct timeval now = { 0, 0 }; - struct timeval interval = { 0, 0 }; - - if(a2dp->bandwithtimestamp.tv_sec == 0) - gettimeofday(&a2dp->bandwithtimestamp, NULL); - - /* See if we must wait again */ - gettimeofday(&now, NULL); - timersub(&now, &a2dp->bandwithtimestamp, &interval); - if(interval.tv_sec > 0) - printf("Bandwith: %d (%d kbps)\n", a2dp->bandwithcount, - a2dp->bandwithcount / 128); - a2dp->bandwithtimestamp = now; - a2dp->bandwithcount = 0; - } - - a2dp->bandwithcount += written; - -#endif - -done: /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; a2dp->samples = 0; a2dp->seq_num++; - if (written > 0) - return written; - return ret; } @@ -497,9 +592,35 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, uint8_t *buff; static int codesize = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," - "io->nonblock=%u", areas->step, areas->first, - offset, size, io->nonblock); + DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu" + , areas->step, areas->first, offset, size); + DBG("hw_ptr = %lu, appl_ptr = %lu" + , io->hw_ptr, io->appl_ptr); + + if(io->hw_ptr > io->appl_ptr) { + ret = bluetooth_a2dp_playback_stop(io); + if(ret == 0) + ret = -EPIPE; + goto done; + } + + /* Check if we should autostart */ + if(io->state == SND_PCM_STATE_PREPARED) { + snd_pcm_sw_params_t *swparams; + snd_pcm_uframes_t threshold; + + snd_pcm_sw_params_malloc(&swparams); + if(!snd_pcm_sw_params_current(io->pcm, swparams) + && !snd_pcm_sw_params_get_start_threshold(swparams, + &threshold) ) { + if(io->appl_ptr >= threshold) { + ret = snd_pcm_start(io->pcm); + if(ret != 0) + goto done; + } + } + snd_pcm_sw_params_free(swparams); + } if (codesize == 0) { /* How much data can be encoded by sbc at a time? */ @@ -548,7 +669,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { - ret = avdtp_write(data, io->nonblock); + ret = avdtp_write(data); if (ret < 0) { if (-ret == EPIPE) ret = -EIO; @@ -561,55 +682,60 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, a2dp->frame_count++; a2dp->samples += encoded / frame_size; a2dp->nsamples += encoded / frame_size; - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + encoded / frame_size) - % io->buffer_size; ret = frames_to_read; done: - DBG("returning %lu", ret); + DBG("returning %ld", ret); return ret; } static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_write, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_write, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_read, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_hsp_read, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_write, + .start = bluetooth_a2dp_playback_start, + .stop = bluetooth_a2dp_playback_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_write, + .poll_descriptors = bluetooth_a2dp_playback_poll_descriptors, + .poll_revents = bluetooth_a2dp_playback_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_read, + .start = bluetooth_start, + .stop = bluetooth_stop, + .pointer = bluetooth_pointer, + .close = bluetooth_close, + .hw_params = bluetooth_a2dp_hw_params, + .prepare = bluetooth_prepare, + .transfer = bluetooth_a2dp_read, + .poll_descriptors = bluetooth_poll_descriptors, + .poll_revents = bluetooth_poll_revents, }; #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) @@ -661,7 +787,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 200); + 2, 50); if (err < 0) return err; @@ -678,13 +804,13 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) .iov_len = sizeof(pkt) }; struct msghdr msgh = { - .msg_name = 0, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg_b, - .msg_controllen = CMSG_LEN(sizeof(int)), - .msg_flags = 0 + .msg_name = 0, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &cmsg_b, + .msg_controllen = CMSG_LEN(sizeof(int)), + .msg_flags = 0 }; ret = recvmsg(data->sock, &msgh, 0); @@ -738,6 +864,14 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; + + if(pipe(a2dp->pipefd) != 0) + return -errno; + if(fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) + return -errno; + if(fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) + return -errno; + return 0; } @@ -888,7 +1022,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) DBG("Bluetooth PCM plugin (%s)", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - data = malloc(sizeof(struct bluetooth_data)); + data = calloc(1, sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; @@ -901,9 +1035,6 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.version = SND_PCM_IOPLUG_VERSION; data->io.name = "Bluetooth Audio Device"; data->io.mmap_rw = 0; /* No direct mmap communication */ - data->io.poll_fd = data->stream_fd; - data->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? - POLLOUT : POLLIN; data->io.private_data = data; if (data->cfg.codec == CFG_CODEC_SBC) -- cgit From cae1e4ecd14d380252b0e00f71bc6cf2887ebd31 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:17:34 +0000 Subject: Fix coding style --- audio/pcm_bluetooth.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index cae080f2..9bae8681 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -41,7 +41,7 @@ #include "ipc.h" #include "sbc.h" -/*#define ENABLE_DEBUG*/ +// #define ENABLE_DEBUG #define BUFFER_SIZE 2048 @@ -246,10 +246,10 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->cfg.codec == CFG_CODEC_SBC) sbc_finish(&data->a2dp.sbc); - if(data->a2dp.pipefd[0] > 0) + if (data->a2dp.pipefd[0] > 0) close(data->a2dp.pipefd[0]); - if(data->a2dp.pipefd[1] > 0) + if (data->a2dp.pipefd[1] > 0) close(data->a2dp.pipefd[1]); free(data); @@ -560,7 +560,7 @@ static int avdtp_write(struct bluetooth_data *data) ret = send(data->stream_fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); - if(ret == -1) + if (ret == -1) ret = -errno; /* Kernel side l2cap socket layer makes sure either everything @@ -597,25 +597,25 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("hw_ptr = %lu, appl_ptr = %lu" , io->hw_ptr, io->appl_ptr); - if(io->hw_ptr > io->appl_ptr) { + if (io->hw_ptr > io->appl_ptr) { ret = bluetooth_a2dp_playback_stop(io); - if(ret == 0) + if (ret == 0) ret = -EPIPE; goto done; } /* Check if we should autostart */ - if(io->state == SND_PCM_STATE_PREPARED) { + if (io->state == SND_PCM_STATE_PREPARED) { snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t threshold; snd_pcm_sw_params_malloc(&swparams); - if(!snd_pcm_sw_params_current(io->pcm, swparams) + if (!snd_pcm_sw_params_current(io->pcm, swparams) && !snd_pcm_sw_params_get_start_threshold(swparams, &threshold) ) { - if(io->appl_ptr >= threshold) { + if (io->appl_ptr >= threshold) { ret = snd_pcm_start(io->pcm); - if(ret != 0) + if (ret != 0) goto done; } } @@ -865,11 +865,11 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.bitpool = sbc->bitpool; - if(pipe(a2dp->pipefd) != 0) + if (pipe(a2dp->pipefd) != 0) return -errno; - if(fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) + if (fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) return -errno; - if(fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) + if (fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) return -errno; return 0; -- cgit From d595791da3d0ce8a20e638f3d0648ccdf78bb55a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:25:47 +0000 Subject: More cleanup --- audio/pcm_bluetooth.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 9bae8681..0547ee3c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -863,13 +863,14 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.subbands = sbc->subbands; a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; + a2dp->pipefd[0] = -1; + a2dp->pipefd[1] = -1; - - if (pipe(a2dp->pipefd) != 0) + if (pipe(a2dp->pipefd) < 0) return -errno; - if (fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) != 0) + if (fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) < 0) return -errno; - if (fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) != 0) + if (fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) < 0) return -errno; return 0; @@ -1022,7 +1023,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) DBG("Bluetooth PCM plugin (%s)", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - data = calloc(1, sizeof(struct bluetooth_data)); + data = malloc(sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; -- cgit From 7090ecad23e84f232648368331cbf41e8d71b6c6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:31:49 +0000 Subject: More coding style cleanup --- audio/pcm_bluetooth.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 0547ee3c..31ad2e18 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -211,17 +211,15 @@ static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) err = pthread_cancel(a2dp_data->hw_thread); if (err != 0) - goto failed; + return -err; err = pthread_join(a2dp_data->hw_thread, 0); if (err != 0) - goto failed; + return -err; a2dp_data->hw_thread = 0; -failed: - DBG(" - return %d", -err); - return -err; + return 0; } static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) @@ -311,6 +309,7 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, return 0; err = errno; + SNDERR("%s (%d)", strerror(err), err); return -err; -- cgit From ca4300477c8c1908803952fafedcba35dd2bae0a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:40:27 +0000 Subject: Fix pthread function error checking --- audio/pcm_bluetooth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 31ad2e18..b971d0ea 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -210,11 +210,11 @@ static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) return 0; err = pthread_cancel(a2dp_data->hw_thread); - if (err != 0) + if (err > 0) return -err; err = pthread_join(a2dp_data->hw_thread, 0); - if (err != 0) + if (err > 0) return -err; a2dp_data->hw_thread = 0; -- cgit From ab0c221fff20faaaf6060679f8002d65ecb36253 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:41:56 +0000 Subject: memcpy_changeendian shouldn't be public --- audio/pcm_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b971d0ea..425d27fb 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -109,7 +109,7 @@ struct bluetooth_data { struct bluetooth_a2dp a2dp; /* a2dp data */ }; -void memcpy_changeendian(void *dst, const void *src, int size) +static void memcpy_changeendian(void *dst, const void *src, int size) { int i; const uint16_t *ptrsrc = src; -- cgit From def42a87ca37b9a656c4ba43389fd191fa6d7fcc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 21 Aug 2007 13:46:34 +0000 Subject: Remove unnecessary assert --- audio/pcm_bluetooth.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 425d27fb..a1184a5b 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -562,13 +562,6 @@ static int avdtp_write(struct bluetooth_data *data) if (ret == -1) ret = -errno; - /* Kernel side l2cap socket layer makes sure either everything - is buffered for sending, or nothing is buffered. - This assertion is to remind people of this fact (and be noticed - the day that changes) - */ - assert(ret < 0 || ret == a2dp->count); - /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->frame_count = 0; -- cgit From de584ba3b958b48a123dde4e6042af11ff8a196f Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 21 Aug 2007 13:50:36 +0000 Subject: Fix useless tab and spaces left. --- audio/pcm_bluetooth.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a1184a5b..12d2e4cc 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -94,7 +94,7 @@ struct bluetooth_a2dp { uint16_t seq_num; /* */ int frame_count; /* */ - pthread_t hw_thread; /* Makes virtual hw pointer move */ + pthread_t hw_thread; /* Makes virtual hw pointer move */ int pipefd[2]; /* Inter thread communication */ }; @@ -171,12 +171,12 @@ static void *a2dp_playback_hw_thread(void *param) pthread_testcancel(); prev_periods = periods; - } + } usleep(period_time); /* Offer opportunity to be canceled by main thread */ - pthread_testcancel(); + pthread_testcancel(); } } static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) @@ -200,7 +200,7 @@ static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp_data = &data->a2dp; + struct bluetooth_a2dp *a2dp_data = &data->a2dp; int err; DBG("%p", io); @@ -246,10 +246,10 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->a2dp.pipefd[0] > 0) close(data->a2dp.pipefd[0]); - + if (data->a2dp.pipefd[1] > 0) close(data->a2dp.pipefd[1]); - + free(data); } @@ -281,7 +281,7 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - /* a2dp : wake up any client polling at us */ + /* a2dp : wake up any client polling at us */ return write(data->a2dp.pipefd[1], &c, 1); } @@ -368,7 +368,7 @@ static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, return 0; } -static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, +static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int space) { @@ -591,16 +591,16 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, if (io->hw_ptr > io->appl_ptr) { ret = bluetooth_a2dp_playback_stop(io); - if (ret == 0) + if (ret == 0) ret = -EPIPE; - goto done; - } + goto done; + } /* Check if we should autostart */ if (io->state == SND_PCM_STATE_PREPARED) { snd_pcm_sw_params_t *swparams; snd_pcm_uframes_t threshold; - + snd_pcm_sw_params_malloc(&swparams); if (!snd_pcm_sw_params_current(io->pcm, swparams) && !snd_pcm_sw_params_get_start_threshold(swparams, @@ -608,10 +608,10 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, if (io->appl_ptr >= threshold) { ret = snd_pcm_start(io->pcm); if (ret != 0) - goto done; + goto done; } } - snd_pcm_sw_params_free(swparams); + snd_pcm_sw_params_free(swparams); } if (codesize == 0) { @@ -861,7 +861,7 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, if (pipe(a2dp->pipefd) < 0) return -errno; if (fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) < 0) - return -errno; + return -errno; if (fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) < 0) return -errno; -- cgit From 38c5be0cc3029bf78445be15470d18343a5b4dae Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 21 Aug 2007 14:03:34 +0000 Subject: Another big coding style fixup --- audio/pcm_bluetooth.c | 154 +++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 89 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 12d2e4cc..80afd562 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -41,7 +41,7 @@ #include "ipc.h" #include "sbc.h" -// #define ENABLE_DEBUG +//#define ENABLE_DEBUG #define BUFFER_SIZE 2048 @@ -106,7 +106,7 @@ struct bluetooth_data { int sock; /* Daemon unix socket */ uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ - struct bluetooth_a2dp a2dp; /* a2dp data */ + struct bluetooth_a2dp a2dp; /* A2DP data */ }; static void memcpy_changeendian(void *dst, const void *src, int size) @@ -161,10 +161,10 @@ static void *a2dp_playback_hw_thread(void *param) char c = 'w'; data->hw_ptr += (periods - prev_periods) * - data->io.period_size; + data->io.period_size; data->hw_ptr %= data->io.buffer_size; - DBG("pointer = %ld", data->hw_ptr); + DBG("pointer=%ld", data->hw_ptr); /* Notify user that hardware pointer has moved */ if (write(data->a2dp.pipefd[1], &c, 1) < 0) @@ -190,9 +190,7 @@ static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) assert(a2dp_data->hw_thread == 0); err = pthread_create(&a2dp_data->hw_thread, 0, - a2dp_playback_hw_thread, data); - - DBG(" - return %d", -err); + a2dp_playback_hw_thread, data); return -err; } @@ -226,10 +224,6 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; -#if 0 - DBG("bluetooth_pointer %p, hw_ptr=%lu", io, data->hw_ptr); -#endif - return data->hw_ptr; } @@ -269,8 +263,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) struct bluetooth_data *data = io->private_data; char c = 'w'; - DBG("Preparing with io->period_size = %lu, io->buffer_size = %lu", - io->period_size, io->buffer_size); + DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", + io->period_size, io->buffer_size); if (io->stream == SND_PCM_STREAM_PLAYBACK) /* If not null for playback, xmms doesn't display time @@ -292,20 +286,20 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, uint32_t period_count = io->buffer_size / io->period_size; int opt_name, err; - DBG("fd = %d, period_count = %d", data->stream_fd, period_count); + DBG("fd=%d period_count=%d", data->stream_fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SCO_TXBUFS : SCO_RXBUFS; + SCO_TXBUFS : SCO_RXBUFS; if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) + sizeof(period_count)) == 0) return 0; opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDBUF : SO_RCVBUF; + SO_SNDBUF : SO_RCVBUF; if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) + sizeof(period_count)) == 0) return 0; err = errno; @@ -323,13 +317,13 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, int opt_name, err; struct timeval t = { 0, period_count }; - DBG("fd = %d, period_count = %d", data->stream_fd, period_count); + DBG("fd=%d period_count=%d", data->stream_fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDTIMEO : SO_RCVTIMEO; + SO_SNDTIMEO : SO_RCVTIMEO; if (setsockopt(data->stream_fd, SOL_SOCKET, opt_name, &t, - sizeof(t)) == 0) + sizeof(t)) == 0) return 0; err = errno; @@ -359,7 +353,7 @@ static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) + unsigned short *revents) { assert(pfds && nfds == 1 && revents); @@ -369,8 +363,7 @@ static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, } static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, - unsigned int space) + struct pollfd *pfd, unsigned int space) { struct bluetooth_data *data = io->private_data; @@ -389,9 +382,8 @@ static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, } static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, - struct pollfd *pfds, - unsigned int nfds, - unsigned short *revents) + struct pollfd *pfds, unsigned int nfds, + unsigned short *revents) { static char buf[1]; int ret; @@ -413,9 +405,8 @@ static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -423,9 +414,8 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, unsigned char *buff; int nrecv, frame_size = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," - "io->nonblock=%u", areas->step, areas->first, offset, size, - io->nonblock); + DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u", + areas->step, areas->first, offset, size, io->nonblock); if (data->count > 0) goto proceed; @@ -448,7 +438,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, /* Increment hardware transmition pointer */ data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % - io->buffer_size; + io->buffer_size; proceed: buff = (unsigned char *) areas->addr + @@ -472,9 +462,8 @@ done: } static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -483,9 +472,8 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, uint8_t *buff; int rsend, frame_size; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu," - "io->nonblock=%u", areas->step, areas->first, - offset, size, io->nonblock); + DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u", + areas->step, areas->first, offset, size, io->nonblock); frame_size = areas->step / 8; if ((data->count + size * frame_size) <= cfg.pkt_len) @@ -493,7 +481,7 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, else frames_to_read = (cfg.pkt_len - data->count) / frame_size; - DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); + DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); /* Ready for more data */ buff = (uint8_t *) areas->addr + @@ -529,9 +517,8 @@ done: } static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { snd_pcm_uframes_t ret = 0; return ret; @@ -545,6 +532,7 @@ static int avdtp_write(struct bluetooth_data *data) struct bluetooth_a2dp *a2dp = &data->a2dp; DBG(""); + header = (void *) a2dp->buffer; payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -557,8 +545,7 @@ static int avdtp_write(struct bluetooth_data *data) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - ret = send(data->stream_fd, a2dp->buffer, a2dp->count, - MSG_DONTWAIT); + ret = send(data->stream_fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); if (ret == -1) ret = -errno; @@ -572,9 +559,8 @@ static int avdtp_write(struct bluetooth_data *data) } static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct bluetooth_a2dp *a2dp = &data->a2dp; @@ -584,10 +570,9 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, uint8_t *buff; static int codesize = 0; - DBG("areas->step=%u, areas->first=%u, offset=%lu, size=%lu" - , areas->step, areas->first, offset, size); - DBG("hw_ptr = %lu, appl_ptr = %lu" - , io->hw_ptr, io->appl_ptr); + DBG("areas->step=%u areas->first=%u offset=%lu size=%lu", + areas->step, areas->first, offset, size); + DBG("hw_ptr=%lu appl_ptr=%lu", io->hw_ptr, io->appl_ptr); if (io->hw_ptr > io->appl_ptr) { ret = bluetooth_a2dp_playback_stop(io); @@ -602,9 +587,8 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, snd_pcm_uframes_t threshold; snd_pcm_sw_params_malloc(&swparams); - if (!snd_pcm_sw_params_current(io->pcm, swparams) - && !snd_pcm_sw_params_get_start_threshold(swparams, - &threshold) ) { + if (!snd_pcm_sw_params_current(io->pcm, swparams) && + !snd_pcm_sw_params_get_start_threshold(swparams, &threshold)) { if (io->appl_ptr >= threshold) { ret = snd_pcm_start(io->pcm); if (ret != 0) @@ -617,7 +601,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, if (codesize == 0) { /* How much data can be encoded by sbc at a time? */ codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; + a2dp->sbc.channels * 2; /* Reserv header space in outgoing buffer */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); @@ -630,17 +614,16 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, else frames_to_read = (codesize - data->count) / frame_size; - DBG("count = %d, frames_to_read = %lu", data->count, frames_to_read); - DBG("a2dp.count = %d cfg.pkt_len = %d", a2dp->count, - data->cfg.pkt_len); + DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); + DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); /* FIXME: If state is not streaming then return */ /* Ready for more data */ buff = (uint8_t *) areas->addr + - (areas->first + areas->step * offset) / 8; + (areas->first + areas->step * offset) / 8; memcpy_changeendian(data->buffer + data->count, buff, - frame_size * frames_to_read); + frame_size * frames_to_read); /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; @@ -658,7 +641,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, data->count -= encoded; - DBG("encoded = %d a2dp.sbc.len= %d", encoded, a2dp->sbc.len); + DBG("encoded=%d a2dp.sbc.len=%d", encoded, a2dp->sbc.len); if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { ret = avdtp_write(data); @@ -762,24 +745,24 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) /* supported channels */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - cfg.channels, cfg.channels); + cfg.channels, cfg.channels); if (err < 0) return err; /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + cfg.rate, cfg.rate); if (err < 0) return err; /* supported block size */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - cfg.pkt_len, cfg.pkt_len); + cfg.pkt_len, cfg.pkt_len); if (err < 0) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 50); + 2, 50); if (err < 0) return err; @@ -794,7 +777,7 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) struct iovec iov = { .iov_base = &pkt, .iov_len = sizeof(pkt) - }; + }; struct msghdr msgh = { .msg_name = 0, .msg_namelen = 0, @@ -816,16 +799,15 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) struct cmsghdr *cmsg; /* Receive auxiliary data in msgh */ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh,cmsg)) { + cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) data->stream_fd = (*(int *) CMSG_DATA(cmsg)); - DBG("stream_fd = %d", data->stream_fd); + DBG("stream_fd=%d", data->stream_fd); return 0; } - } - else - SNDERR("Unexpected packet type received: type = %d", pkt.type); + } else + SNDERR("Unexpected packet type %d received", pkt.type); return -EINVAL; } @@ -902,14 +884,12 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) total = ret; if (pkt->type != PKT_TYPE_CFG_RSP) { - SNDERR("Unexpected packet type received: type = %d", - pkt->type); + SNDERR("Unexpected packet type %d received", pkt->type); return -EINVAL; } if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error while configuring device: error = %d", - pkt->error); + SNDERR("Error %d while configuring device", pkt->error); return pkt->error; } @@ -928,8 +908,7 @@ done: DBG("OK - %d bytes received", total); if (pkt->length != (total - sizeof(struct ipc_packet))) { - SNDERR("Error while configuring device: packet size doesn't " - "match"); + SNDERR("Error while configuring device: packet size doesn't match"); return -EINVAL; } @@ -937,25 +916,22 @@ done: DBG("Device configuration:"); - DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n" - "\tsample_size=%u\n\trate=%u", data->stream_fd, - data->cfg.fd_opt, data->cfg.channels, data->cfg.pkt_len, - data->cfg.sample_size, data->cfg.rate); + DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", + data->stream_fd, data->cfg.fd_opt, data->cfg.channels, + data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); if (data->cfg.codec == CFG_CODEC_SBC) { struct bluetooth_a2dp *a2dp = &data->a2dp; ret = bluetooth_a2dp_init(data, sbc); if (ret < 0) return ret; - printf("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\t" - "bitpool=%u\n", a2dp->sbc.allocation, - a2dp->sbc.subbands, a2dp->sbc.blocks, - a2dp->sbc.bitpool); + printf("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, + a2dp->sbc.blocks, a2dp->sbc.bitpool); } if (data->stream_fd == -1) { - SNDERR("Error while configuring device: could not acquire " - "audio socket"); + SNDERR("Error while configuring device: could not acquire audio socket"); return -EINVAL; } -- cgit From b1618922db92f9bc65b0841f66eb71742bc1b553 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 21 Aug 2007 21:32:09 +0000 Subject: Add swap member to sbc struct and fix pcm plugin to not swap the buffer. --- audio/pcm_bluetooth.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 80afd562..495f1521 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -109,16 +109,6 @@ struct bluetooth_data { struct bluetooth_a2dp a2dp; /* A2DP data */ }; -static void memcpy_changeendian(void *dst, const void *src, int size) -{ - int i; - const uint16_t *ptrsrc = src; - uint16_t *ptrdst = dst; - - for (i = 0; i < size / 2; i++) - *ptrdst++ = htons(*ptrsrc++); -} - static int bluetooth_start(snd_pcm_ioplug_t *io) { DBG("bluetooth_start %p", io); @@ -622,8 +612,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, /* Ready for more data */ buff = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8; - memcpy_changeendian(data->buffer + data->count, buff, - frame_size * frames_to_read); + memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; -- cgit From 714cd0ab444bd51731131866463f5c565c8a33f6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Aug 2007 11:13:27 +0000 Subject: Use "unsigned int" instead of "unsigned long long" and reset the point of reference at proper intervals to avoid overflow --- audio/pcm_bluetooth.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 495f1521..77c8045e 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -25,6 +25,7 @@ #include #endif +#include #include #include #include @@ -43,6 +44,10 @@ //#define ENABLE_DEBUG +#define UINT_SECS_MAX (UINT_MAX / 1000000 - 1) + +#define MIN_PERIOD_TIME 1000 + #define BUFFER_SIZE 2048 #ifdef ENABLE_DEBUG @@ -136,8 +141,7 @@ static void *a2dp_playback_hw_thread(void *param) gettimeofday(&start, 0); while (1) { - unsigned long long dtime; - unsigned int periods; + unsigned int dtime, periods; struct timeval cur, delta; gettimeofday(&cur, 0); @@ -160,10 +164,16 @@ static void *a2dp_playback_hw_thread(void *param) if (write(data->a2dp.pipefd[1], &c, 1) < 0) pthread_testcancel(); - prev_periods = periods; + /* Reset point of reference to avoid too big values + * that wont fit an unsigned int */ + if (delta.tv_sec > UINT_SECS_MAX) { + prev_periods = 0; + gettimeofday(&start, 0); + } else + prev_periods = periods; } - usleep(period_time); + usleep(MIN_PERIOD_TIME); /* Offer opportunity to be canceled by main thread */ pthread_testcancel(); -- cgit From f883b95e5ca06f863a75a1e2b4d2ec071bb30112 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Aug 2007 12:04:21 +0000 Subject: Don't restart playback thread when stop and start are called --- audio/pcm_bluetooth.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 77c8045e..2748c8f4 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -112,6 +112,7 @@ struct bluetooth_data { uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ + int stopped; }; static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -144,6 +145,9 @@ static void *a2dp_playback_hw_thread(void *param) unsigned int dtime, periods; struct timeval cur, delta; + if (data->stopped) + goto iter_sleep; + gettimeofday(&cur, 0); timersub(&cur, &start, &delta); @@ -173,6 +177,7 @@ static void *a2dp_playback_hw_thread(void *param) prev_periods = periods; } +iter_sleep: usleep(MIN_PERIOD_TIME); /* Offer opportunity to be canceled by main thread */ @@ -187,7 +192,10 @@ static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) DBG("%p", io); - assert(a2dp_data->hw_thread == 0); + data->stopped = 0; + + if (a2dp_data->hw_thread) + return 0; err = pthread_create(&a2dp_data->hw_thread, 0, a2dp_playback_hw_thread, data); @@ -198,24 +206,10 @@ static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp_data = &data->a2dp; - int err; DBG("%p", io); - /* Beware - We can be called more than once */ - if (a2dp_data->hw_thread == 0) - return 0; - - err = pthread_cancel(a2dp_data->hw_thread); - if (err > 0) - return -err; - - err = pthread_join(a2dp_data->hw_thread, 0); - if (err > 0) - return -err; - - a2dp_data->hw_thread = 0; + data->stopped = 1; return 0; } @@ -235,8 +229,16 @@ static void bluetooth_exit(struct bluetooth_data *data) if (data->stream_fd >= 0) close(data->stream_fd); - if (data->cfg.codec == CFG_CODEC_SBC) - sbc_finish(&data->a2dp.sbc); + if (data->cfg.codec == CFG_CODEC_SBC) { + struct bluetooth_a2dp *a2dp = &data->a2dp; + + if (a2dp->hw_thread) { + pthread_cancel(a2dp->hw_thread); + pthread_join(a2dp->hw_thread, 0); + } + + sbc_finish(&a2dp->sbc); + } if (data->a2dp.pipefd[0] > 0) close(data->a2dp.pipefd[0]); -- cgit From 8dfa81ccf2c3eeb7518958428524a9850be1fd3d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Aug 2007 12:50:41 +0000 Subject: Make the uint boundary check a little bit more reliable --- audio/pcm_bluetooth.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2748c8f4..d5f89143 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -162,19 +162,18 @@ static void *a2dp_playback_hw_thread(void *param) data->io.period_size; data->hw_ptr %= data->io.buffer_size; - DBG("pointer=%ld", data->hw_ptr); - /* Notify user that hardware pointer has moved */ if (write(data->a2dp.pipefd[1], &c, 1) < 0) pthread_testcancel(); /* Reset point of reference to avoid too big values * that wont fit an unsigned int */ - if (delta.tv_sec > UINT_SECS_MAX) { + if (delta.tv_sec < UINT_SECS_MAX) + prev_periods = periods; + else { prev_periods = 0; gettimeofday(&start, 0); - } else - prev_periods = periods; + } } iter_sleep: -- cgit From 656d22e0c137ed29481d164c018c58137b610e52 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 22 Aug 2007 13:24:35 +0000 Subject: stopped variable is A2DP specific (at least for now) --- audio/pcm_bluetooth.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d5f89143..5e4da877 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -101,6 +101,7 @@ struct bluetooth_a2dp { pthread_t hw_thread; /* Makes virtual hw pointer move */ int pipefd[2]; /* Inter thread communication */ + int stopped; }; struct bluetooth_data { @@ -112,7 +113,6 @@ struct bluetooth_data { uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ - int stopped; }; static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -132,6 +132,7 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io) static void *a2dp_playback_hw_thread(void *param) { struct bluetooth_data *data = param; + struct bluetooth_a2dp *a2dp = &data->a2dp; unsigned int prev_periods; double period_time; struct timeval start; @@ -145,7 +146,7 @@ static void *a2dp_playback_hw_thread(void *param) unsigned int dtime, periods; struct timeval cur, delta; - if (data->stopped) + if (a2dp->stopped) goto iter_sleep; gettimeofday(&cur, 0); @@ -163,7 +164,7 @@ static void *a2dp_playback_hw_thread(void *param) data->hw_ptr %= data->io.buffer_size; /* Notify user that hardware pointer has moved */ - if (write(data->a2dp.pipefd[1], &c, 1) < 0) + if (write(a2dp->pipefd[1], &c, 1) < 0) pthread_testcancel(); /* Reset point of reference to avoid too big values @@ -186,17 +187,17 @@ iter_sleep: static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp_data = &data->a2dp; + struct bluetooth_a2dp *a2dp = &data->a2dp; int err; DBG("%p", io); - data->stopped = 0; + a2dp->stopped = 0; - if (a2dp_data->hw_thread) + if (a2dp->hw_thread) return 0; - err = pthread_create(&a2dp_data->hw_thread, 0, + err = pthread_create(&a2dp->hw_thread, 0, a2dp_playback_hw_thread, data); return -err; @@ -205,10 +206,11 @@ static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp = &data->a2dp; DBG("%p", io); - data->stopped = 1; + a2dp->stopped = 1; return 0; } -- cgit From 674a1d6355ebd5c1e9ffc2e7663b747f60ac70b5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Aug 2007 14:09:24 +0000 Subject: Add skeleton for GStreamer plugin --- audio/Makefile.am | 16 +++++++++++++++- audio/gstbluetooth.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 audio/gstbluetooth.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index cc73f9be..78352073 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -26,14 +26,28 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ +libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ +libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ endif + +if GSTREAMER +gstreamerdir = $(libdir)/gstreamer-0.10 + +gstreamer_LTLIBRARIES = libgstbluetooth.la + +libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h +libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined +libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ +libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ +endif + endif -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @ALSA_CFLAGS@ @SBC_CFLAGS@ +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ INCLUDES = -I$(top_srcdir)/common diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c new file mode 100644 index 00000000..4169217b --- /dev/null +++ b/audio/gstbluetooth.c @@ -0,0 +1,41 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "ipc.h" + +static gboolean plugin_init(GstPlugin *plugin) +{ + return TRUE; +} + +GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, + "bluetooth", "Bluetooth plugin library", + plugin_init, VERSION, "LGPL", PACKAGE, "http://www.bluez.org/") -- cgit From 84aca873392c957faa5974485633ce4081a416e2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 22 Aug 2007 14:15:43 +0000 Subject: Add Playing and Stopped signals to sink inteface. --- audio/audio-api.txt | 8 ++++++++ audio/sink.c | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 7e06b1f6..6a4041c0 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -245,6 +245,14 @@ Signals void Connected() Sent when the device has been disconnected from. + void Playing() + + Sent when a stream with remote device is started. + + void Stopped() + + Sent when a stream with remote device is suspended. + org.bluez.audio.Source interface ================================ diff --git a/audio/sink.c b/audio/sink.c index c09c019f..8141eff5 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -113,9 +113,19 @@ static void stream_state_changed(struct avdtp_stream *stream, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); + else if (old_state == AVDTP_STATE_STREAMING) + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + "Stopped", + DBUS_TYPE_INVALID); break; - case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_STREAMING: + dbus_connection_emit_signal(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + "Playing", + DBUS_TYPE_INVALID); + break; + case AVDTP_STATE_CONFIGURED: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: default: @@ -259,6 +269,8 @@ static DBusMethodVTable sink_methods[] = { static DBusSignalVTable sink_signals[] = { { "Connected", "" }, { "Disconnected", "" }, + { "Playing", "" }, + { "Stopped", "" }, { NULL, NULL } }; -- cgit From 54fd195933b94a5ec4f3885299e3279668d1ea66 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Aug 2007 14:55:25 +0000 Subject: Include SBC header file --- audio/gstbluetooth.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 4169217b..ece6aa9e 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -30,6 +30,7 @@ #include #include "ipc.h" +#include "sbc.h" static gboolean plugin_init(GstPlugin *plugin) { -- cgit From 9a2408e2e45e5b47c3f7019246d9260d7f864f4f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Aug 2007 14:57:02 +0000 Subject: Remove empty line --- audio/Makefile.am | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 78352073..5b63d226 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -44,7 +44,6 @@ libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -no-undefine libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif - endif AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ -- cgit From 33fcbce4ce2c8b409fd242c64e45c664098e66b9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 22 Aug 2007 19:37:48 +0000 Subject: Code cleanup. --- audio/pcm_bluetooth.c | 96 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 23 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 5e4da877..c6df710b 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -89,15 +89,14 @@ struct rtp_payload { struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ + int codesize; /* SBC codesize */ int samples; /* Number of encoded samples */ - time_t timestamp; /* Codec samples timestamp */ uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ int count; /* Codec transfer buffer counter */ int nsamples; /* Cumulative number of codec samples */ - struct timeval ntimestamp; /* Cumulative timeval */ - uint16_t seq_num; /* */ - int frame_count; /* */ + uint16_t seq_num; /* Cumulative packet sequence */ + int frame_count; /* Current frames in buffer*/ pthread_t hw_thread; /* Makes virtual hw pointer move */ int pipefd[2]; /* Inter thread communication */ @@ -184,6 +183,7 @@ iter_sleep: pthread_testcancel(); } } + static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -534,8 +534,6 @@ static int avdtp_write(struct bluetooth_data *data) struct rtp_payload *payload; struct bluetooth_a2dp *a2dp = &data->a2dp; - DBG(""); - header = (void *) a2dp->buffer; payload = (void *) (a2dp->buffer + sizeof(*header)); @@ -571,7 +569,6 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, snd_pcm_uframes_t frames_to_read; int frame_size, encoded; uint8_t *buff; - static int codesize = 0; DBG("areas->step=%u areas->first=%u offset=%lu size=%lu", areas->step, areas->first, offset, size); @@ -601,21 +598,11 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, snd_pcm_sw_params_free(swparams); } - if (codesize == 0) { - /* How much data can be encoded by sbc at a time? */ - codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; - /* Reserv header space in outgoing buffer */ - a2dp->count = sizeof(struct rtp_header) + - sizeof(struct rtp_payload); - gettimeofday(&a2dp->ntimestamp, NULL); - } - frame_size = areas->step / 8; - if ((data->count + size * frame_size) <= codesize) + if ((data->count + size * frame_size) <= a2dp->codesize) frames_to_read = size; else - frames_to_read = (codesize - data->count) / frame_size; + frames_to_read = (a2dp->codesize - data->count) / frame_size; DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); @@ -629,13 +616,13 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; - if (data->count != codesize) { + if (data->count != a2dp->codesize) { ret = frames_to_read; goto done; } /* Enough data to encode (sbc wants 1k blocks) */ - encoded = sbc_encode(&(a2dp->sbc), data->buffer, codesize); + encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize); if (encoded <= 0) { DBG("Encoding error %d", encoded); goto done; @@ -717,7 +704,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { #define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) -static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) +static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -763,6 +750,61 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, + 2, 200); + if (err < 0) + return err; + + return 0; +} + +static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp = &data->a2dp; + struct ipc_data_cfg cfg = data->cfg; + snd_pcm_access_t access_list[] = { + SND_PCM_ACCESS_RW_INTERLEAVED, + /* Mmap access is really useless fo this driver, but we + * support it because some pieces of software out there + * insist on using it */ + SND_PCM_ACCESS_MMAP_INTERLEAVED + }; + unsigned int format_list[] = { + SND_PCM_FORMAT_S16_LE + }; + int err; + + /* access type */ + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, + ARRAY_NELEMS(access_list), access_list); + if (err < 0) + return err; + + /* supported formats */ + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, + ARRAY_NELEMS(format_list), format_list); + if (err < 0) + return err; + + /* supported channels */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, + cfg.channels, cfg.channels); + if (err < 0) + return err; + + /* supported rate */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, + cfg.rate, cfg.rate); + if (err < 0) + return err; + + /* supported block size */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + a2dp->codesize, a2dp->codesize); + if (err < 0) + return err; + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 50); if (err < 0) @@ -771,6 +813,7 @@ static int bluetooth_hw_constraint(snd_pcm_ioplug_t *io) return 0; } + static int bluetooth_recvmsg_fd(struct bluetooth_data *data) { char cmsg_b[CMSG_SPACE(sizeof(int))]; @@ -839,6 +882,9 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.subbands = sbc->subbands; a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; + a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->pipefd[0] = -1; a2dp->pipefd[1] = -1; @@ -1021,7 +1067,11 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) if (err < 0) goto error; - err = bluetooth_hw_constraint(&data->io); + if (data->cfg.codec == CFG_CODEC_SBC) + err = bluetooth_a2dp_hw_constraint(&data->io); + else + err = bluetooth_hsp_hw_constraint(&data->io); + if (err < 0) { snd_pcm_ioplug_delete(&data->io); goto error; -- cgit From 73b35c403df78c10e03ded497fcb16fcb23b822e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Aug 2007 21:50:59 +0000 Subject: Add skeleton for an A2DP sink element --- audio/Makefile.am | 6 +- audio/gsta2dpsink.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++ audio/gsta2dpsink.h | 53 ++++++++++++++++ audio/gstbluetooth.c | 18 ++++-- audio/ipc.h | 2 + 5 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 audio/gsta2dpsink.c create mode 100644 audio/gsta2dpsink.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 5b63d226..ac266ca1 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -39,9 +39,9 @@ gstreamerdir = $(libdir)/gstreamer-0.10 gstreamer_LTLIBRARIES = libgstbluetooth.la -libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h -libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined -libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ +libgstbluetooth_la_SOURCES = gstbluetooth.c gsta2dpsink.h gsta2dpsink.c ipc.h +libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c new file mode 100644 index 00000000..bb033a58 --- /dev/null +++ b/audio/gsta2dpsink.c @@ -0,0 +1,174 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "ipc.h" +#include "sbc.h" + +#include "gsta2dpsink.h" + +GST_DEBUG_CATEGORY_EXTERN(bluetooth_debug); +#define GST_CAT_DEFAULT bluetooth_debug + +GST_BOILERPLATE(GstA2dpSink, gst_a2dpsink, GstAudioSink, GST_TYPE_AUDIO_SINK); + +static const GstElementDetails a2dpsink_details = + GST_ELEMENT_DETAILS("Bluetooth A2DP sink", + "Sink/Audio", + "Plays audio to an A2DP device", + "Marcel Holtmann "); + +static GstStaticPadTemplate sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("ANY")); + +static void gst_a2dpsink_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + GST_DEBUG(""); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sink_factory)); + + gst_element_class_set_details(element_class, &a2dpsink_details); +} + +static void gst_a2dpsink_dispose(GObject *object) +{ + G_OBJECT_CLASS(parent_class)->dispose(object); +} + +static void gst_a2dpsink_finalize(GObject *object) +{ + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void gst_a2dpsink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_a2dpsink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static GstCaps *gst_a2dpsink_getcaps(GstBaseSink *basesink) +{ + GST_DEBUG_OBJECT(basesink, ""); + + return NULL; +} + +static gboolean gst_a2dpsink_open(GstAudioSink *audiosink) +{ + GST_DEBUG_OBJECT(audiosink, ""); + + return TRUE; +} + +static gboolean gst_a2dpsink_prepare(GstAudioSink *audiosink, + GstRingBufferSpec *spec) +{ + GST_DEBUG_OBJECT(audiosink, "spec %p", spec); + + return TRUE; +} + +static gboolean gst_a2dpsink_unprepare(GstAudioSink *audiosink) +{ + GST_DEBUG_OBJECT(audiosink, ""); + + return TRUE; +} + +static gboolean gst_a2dpsink_close(GstAudioSink *audiosink) +{ + GST_DEBUG_OBJECT(audiosink, ""); + + return TRUE; +} + +static guint gst_a2dpsink_write(GstAudioSink *audiosink, + gpointer data, guint length) +{ + GST_DEBUG_OBJECT(audiosink, "data %p length %d", data, length); + + return length; +} + +static guint gst_a2dpsink_delay(GstAudioSink *audiosink) +{ + GST_DEBUG_OBJECT(audiosink, ""); + + return 0; +} + +static void gst_a2dpsink_reset(GstAudioSink *audiosink) +{ +} + +static void gst_a2dpsink_class_init(GstA2dpSinkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS(klass); + GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS(klass); + + GST_DEBUG(""); + + parent_class = g_type_class_peek_parent(klass); + + gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_a2dpsink_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dpsink_finalize); + gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dpsink_get_property); + gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dpsink_set_property); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dpsink_getcaps); + + gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dpsink_open); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dpsink_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dpsink_unprepare); + gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dpsink_close); + gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dpsink_write); + gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dpsink_delay); + gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dpsink_reset); +} + +static void gst_a2dpsink_init(GstA2dpSink *a2dpsink, GstA2dpSinkClass *klass) +{ + GST_DEBUG_OBJECT(a2dpsink, ""); +} diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h new file mode 100644 index 00000000..9630c11a --- /dev/null +++ b/audio/gsta2dpsink.h @@ -0,0 +1,53 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_A2DP_SINK \ + (gst_a2dpsink_get_type()) +#define GST_A2DP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SINK,GstA2dpSink)) +#define GST_A2DP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SINK,GstA2dpSinkClass)) +#define GST_IS_A2DP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SINK)) +#define GST_IS_A2DP_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) + +typedef struct _GstA2dpSink GstA2dpSink; +typedef struct _GstA2dpSinkClass GstA2dpSinkClass; + +struct _GstA2dpSink { + GstAudioSink sink; +}; + +struct _GstA2dpSinkClass { + GstAudioSinkClass parent_class; +}; + +GType gst_a2dpsink_get_type(void); + +G_END_DECLS diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index ece6aa9e..392de194 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -25,18 +25,24 @@ #include #endif -#include +#include "gsta2dpsink.h" -#include - -#include "ipc.h" -#include "sbc.h" +GST_DEBUG_CATEGORY(bluetooth_debug); static gboolean plugin_init(GstPlugin *plugin) { + GST_INFO("Bluetooth plugin %s", VERSION); + + if (gst_element_register(plugin, "a2dpsink", + GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) + return FALSE; + + GST_DEBUG_CATEGORY_INIT(bluetooth_debug, "bluetooth", 0, + "Bluetooth plugin"); + return TRUE; } GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, "bluetooth", "Bluetooth plugin library", - plugin_init, VERSION, "LGPL", PACKAGE, "http://www.bluez.org/") + plugin_init, VERSION, "LGPL", "BlueZ", "http://www.bluez.org/") diff --git a/audio/ipc.h b/audio/ipc.h index 77997496..27ee742c 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -21,6 +21,8 @@ * */ +#include + #define IPC_TYPE_CONNECT 0x0001 #define IPC_MTU 32 -- cgit From d5a08c35bad64c16c4ccd8e9cd5a6d6b3b0150cc Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 23 Aug 2007 10:43:07 +0000 Subject: Don't install GStreamer plugin --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index ac266ca1..f5eb30e9 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -37,7 +37,7 @@ endif if GSTREAMER gstreamerdir = $(libdir)/gstreamer-0.10 -gstreamer_LTLIBRARIES = libgstbluetooth.la +noinst_LTLIBRARIES = libgstbluetooth.la libgstbluetooth_la_SOURCES = gstbluetooth.c gsta2dpsink.h gsta2dpsink.c ipc.h libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -- cgit From 2ec3960a534fe2e8e0d63cc3600ef1fd90505b0d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 23 Aug 2007 18:04:36 +0000 Subject: Update code to match example audio.conf --- audio/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 9e677354..bd9cd0c5 100644 --- a/audio/main.c +++ b/audio/main.c @@ -93,7 +93,7 @@ static void read_config(const char *file) } str = g_key_file_get_string(keyfile, "General", - "Disabled", &err); + "Disable", &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); -- cgit From 0a527f667ae62ac335f22d1d053179b20ff26e94 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 23 Aug 2007 19:12:23 +0000 Subject: Add SBC encoder and decoder skeletons for GStreamer --- audio/Makefile.am | 7 ++++-- audio/gsta2dpsink.c | 68 +++++++++++++++++++++++++++------------------------- audio/gsta2dpsink.h | 4 ++-- audio/gstbluetooth.c | 15 ++++++++---- audio/gstsbcdec.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ audio/gstsbcdec.h | 55 ++++++++++++++++++++++++++++++++++++++++++ audio/gstsbcenc.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ audio/gstsbcenc.h | 55 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 283 insertions(+), 41 deletions(-) create mode 100644 audio/gstsbcdec.c create mode 100644 audio/gstsbcdec.h create mode 100644 audio/gstsbcenc.c create mode 100644 audio/gstsbcenc.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index f5eb30e9..240c737f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -37,9 +37,12 @@ endif if GSTREAMER gstreamerdir = $(libdir)/gstreamer-0.10 -noinst_LTLIBRARIES = libgstbluetooth.la +gstreamer_LTLIBRARIES = libgstbluetooth.la -libgstbluetooth_la_SOURCES = gstbluetooth.c gsta2dpsink.h gsta2dpsink.c ipc.h +libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ + gstsbcdec.h gstsbcdec.c \ + gstsbcenc.h gstsbcenc.c \ + gsta2dpsink.h gsta2dpsink.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index bb033a58..08832524 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -30,12 +30,12 @@ #include "gsta2dpsink.h" -GST_DEBUG_CATEGORY_EXTERN(bluetooth_debug); -#define GST_CAT_DEFAULT bluetooth_debug +GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); +#define GST_CAT_DEFAULT a2dp_sink_debug -GST_BOILERPLATE(GstA2dpSink, gst_a2dpsink, GstAudioSink, GST_TYPE_AUDIO_SINK); +GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstAudioSink, GST_TYPE_AUDIO_SINK); -static const GstElementDetails a2dpsink_details = +static const GstElementDetails a2dp_sink_details = GST_ELEMENT_DETAILS("Bluetooth A2DP sink", "Sink/Audio", "Plays audio to an A2DP device", @@ -45,7 +45,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("ANY")); -static void gst_a2dpsink_base_init(gpointer g_class) +static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -54,20 +54,20 @@ static void gst_a2dpsink_base_init(gpointer g_class) gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_factory)); - gst_element_class_set_details(element_class, &a2dpsink_details); + gst_element_class_set_details(element_class, &a2dp_sink_details); } -static void gst_a2dpsink_dispose(GObject *object) +static void gst_a2dp_sink_dispose(GObject *object) { G_OBJECT_CLASS(parent_class)->dispose(object); } -static void gst_a2dpsink_finalize(GObject *object) +static void gst_a2dp_sink_finalize(GObject *object) { G_OBJECT_CLASS(parent_class)->finalize(object); } -static void gst_a2dpsink_get_property(GObject *object, guint prop_id, +static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { @@ -77,7 +77,7 @@ static void gst_a2dpsink_get_property(GObject *object, guint prop_id, } } -static void gst_a2dpsink_set_property(GObject *object, guint prop_id, +static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { @@ -87,21 +87,21 @@ static void gst_a2dpsink_set_property(GObject *object, guint prop_id, } } -static GstCaps *gst_a2dpsink_getcaps(GstBaseSink *basesink) +static GstCaps *gst_a2dp_sink_getcaps(GstBaseSink *basesink) { GST_DEBUG_OBJECT(basesink, ""); return NULL; } -static gboolean gst_a2dpsink_open(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_open(GstAudioSink *audiosink) { GST_DEBUG_OBJECT(audiosink, ""); return TRUE; } -static gboolean gst_a2dpsink_prepare(GstAudioSink *audiosink, +static gboolean gst_a2dp_sink_prepare(GstAudioSink *audiosink, GstRingBufferSpec *spec) { GST_DEBUG_OBJECT(audiosink, "spec %p", spec); @@ -109,21 +109,21 @@ static gboolean gst_a2dpsink_prepare(GstAudioSink *audiosink, return TRUE; } -static gboolean gst_a2dpsink_unprepare(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_unprepare(GstAudioSink *audiosink) { GST_DEBUG_OBJECT(audiosink, ""); return TRUE; } -static gboolean gst_a2dpsink_close(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_close(GstAudioSink *audiosink) { GST_DEBUG_OBJECT(audiosink, ""); return TRUE; } -static guint gst_a2dpsink_write(GstAudioSink *audiosink, +static guint gst_a2dp_sink_write(GstAudioSink *audiosink, gpointer data, guint length) { GST_DEBUG_OBJECT(audiosink, "data %p length %d", data, length); @@ -131,18 +131,19 @@ static guint gst_a2dpsink_write(GstAudioSink *audiosink, return length; } -static guint gst_a2dpsink_delay(GstAudioSink *audiosink) +static guint gst_a2dp_sink_delay(GstAudioSink *audiosink) { GST_DEBUG_OBJECT(audiosink, ""); return 0; } -static void gst_a2dpsink_reset(GstAudioSink *audiosink) +static void gst_a2dp_sink_reset(GstAudioSink *audiosink) { + GST_DEBUG_OBJECT(audiosink, ""); } -static void gst_a2dpsink_class_init(GstA2dpSinkClass *klass) +static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS(klass); @@ -152,23 +153,26 @@ static void gst_a2dpsink_class_init(GstA2dpSinkClass *klass) parent_class = g_type_class_peek_parent(klass); - gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_a2dpsink_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dpsink_finalize); - gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dpsink_get_property); - gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dpsink_set_property); + gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_a2dp_sink_dispose); + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); + gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_property); + gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_property); + + gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_getcaps); - gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dpsink_getcaps); + gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dp_sink_open); + gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_prepare); + gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unprepare); + gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dp_sink_close); + gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dp_sink_write); + gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dp_sink_delay); + gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dp_sink_reset); - gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dpsink_open); - gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dpsink_prepare); - gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dpsink_unprepare); - gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dpsink_close); - gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dpsink_write); - gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dpsink_delay); - gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dpsink_reset); + GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, + "A2DP sink element"); } -static void gst_a2dpsink_init(GstA2dpSink *a2dpsink, GstA2dpSinkClass *klass) +static void gst_a2dp_sink_init(GstA2dpSink *a2dpsink, GstA2dpSinkClass *klass) { GST_DEBUG_OBJECT(a2dpsink, ""); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 9630c11a..9575bc44 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -27,7 +27,7 @@ G_BEGIN_DECLS #define GST_TYPE_A2DP_SINK \ - (gst_a2dpsink_get_type()) + (gst_a2dp_sink_get_type()) #define GST_A2DP_SINK(obj) \ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SINK,GstA2dpSink)) #define GST_A2DP_SINK_CLASS(klass) \ @@ -48,6 +48,6 @@ struct _GstA2dpSinkClass { GstAudioSinkClass parent_class; }; -GType gst_a2dpsink_get_type(void); +GType gst_a2dp_sink_get_type(void); G_END_DECLS diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 392de194..58655573 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -25,21 +25,26 @@ #include #endif +#include "gstsbcenc.h" +#include "gstsbcdec.h" #include "gsta2dpsink.h" -GST_DEBUG_CATEGORY(bluetooth_debug); - static gboolean plugin_init(GstPlugin *plugin) { GST_INFO("Bluetooth plugin %s", VERSION); + if (gst_element_register(plugin, "sbcenc", + GST_RANK_NONE, GST_TYPE_SBC_ENC) == FALSE) + return FALSE; + + if (gst_element_register(plugin, "sbcdec", + GST_RANK_PRIMARY, GST_TYPE_SBC_DEC) == FALSE) + return FALSE; + if (gst_element_register(plugin, "a2dpsink", GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) return FALSE; - GST_DEBUG_CATEGORY_INIT(bluetooth_debug, "bluetooth", 0, - "Bluetooth plugin"); - return TRUE; } diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c new file mode 100644 index 00000000..9d547eda --- /dev/null +++ b/audio/gstsbcdec.c @@ -0,0 +1,60 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "sbc.h" + +#include "gstsbcdec.h" + +GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug); +#define GST_CAT_DEFAULT sbc_dec_debug + +GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT); + +static const GstElementDetails sbc_dec_details = + GST_ELEMENT_DETAILS("Bluetooth SBC decoder", + "Codec/Decoder/Audio", + "Decode a SBC audio stream", + "Marcel Holtmann "); + +static void gst_sbc_dec_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_set_details(element_class, &sbc_dec_details); +} + +static void gst_sbc_dec_class_init(GstSbcDecClass *klass) +{ + parent_class = g_type_class_peek_parent(klass); + + GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0, + "SBC decoding element"); +} + +static void gst_sbc_dec_init(GstSbcDec *sbcdec, GstSbcDecClass *klass) +{ +} diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h new file mode 100644 index 00000000..0737b9d9 --- /dev/null +++ b/audio/gstsbcdec.h @@ -0,0 +1,55 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SBC_DEC \ + (gst_sbc_dec_get_type()) +#define GST_SBC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec)) +#define GST_SBC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass)) +#define GST_IS_SBC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC)) +#define GST_IS_SBC_DEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC)) + +typedef struct _GstSbcDec GstSbcDec; +typedef struct _GstSbcDecClass GstSbcDecClass; + +struct _GstSbcDec { + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; +}; + +struct _GstSbcDecClass { + GstElementClass parent_class; +}; + +GType gst_sbc_dec_get_type(void); + +G_END_DECLS diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c new file mode 100644 index 00000000..6e63ac45 --- /dev/null +++ b/audio/gstsbcenc.c @@ -0,0 +1,60 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "sbc.h" + +#include "gstsbcenc.h" + +GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); +#define GST_CAT_DEFAULT sbc_enc_debug + +GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); + +static const GstElementDetails sbc_enc_details = + GST_ELEMENT_DETAILS("Bluetooth SBC encoder", + "Codec/Encoder/Audio", + "Encode a SBC audio stream", + "Marcel Holtmann "); + +static void gst_sbc_enc_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_set_details(element_class, &sbc_enc_details); +} + +static void gst_sbc_enc_class_init(GstSbcEncClass *klass) +{ + parent_class = g_type_class_peek_parent(klass); + + GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, + "SBC encoding element"); +} + +static void gst_sbc_enc_init(GstSbcEnc *sbcenc, GstSbcEncClass *klass) +{ +} diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h new file mode 100644 index 00000000..64457d21 --- /dev/null +++ b/audio/gstsbcenc.h @@ -0,0 +1,55 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SBC_ENC \ + (gst_sbc_enc_get_type()) +#define GST_SBC_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_ENC,GstSbcEnc)) +#define GST_SBC_ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_ENC,GstSbcEncClass)) +#define GST_IS_SBC_ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_ENC)) +#define GST_IS_SBC_ENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_ENC)) + +typedef struct _GstSbcEnc GstSbcEnc; +typedef struct _GstSbcEncClass GstSbcEncClass; + +struct _GstSbcEnc { + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; +}; + +struct _GstSbcEncClass { + GstElementClass parent_class; +}; + +GType gst_sbc_enc_get_type(void); + +G_END_DECLS -- cgit From 92f94938b981fe6a892e365670d5fa4d58c94283 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 23 Aug 2007 23:37:15 +0000 Subject: Add support for ALSA parameters --- audio/Makefile.am | 4 ++-- audio/device.c | 23 ++++++++++++++++++ audio/device.h | 2 ++ audio/ipc.h | 10 ++++---- audio/manager.c | 65 ++++++++++++++++++++++++++++++++++----------------- audio/manager.h | 7 +++--- audio/pcm_bluetooth.c | 47 +++++++++++++++++++++++++++++++++---- audio/unix.c | 43 +++++++++++++++++++++------------- 8 files changed, 148 insertions(+), 53 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 240c737f..ce16cd12 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -25,8 +25,8 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ -libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ +libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @BLUEZ_LIBS@ @ALSA_LIBS@ +libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic diff --git a/audio/device.c b/audio/device.c index 2a6498ee..2c06233e 100644 --- a/audio/device.c +++ b/audio/device.c @@ -417,3 +417,26 @@ uint8_t device_get_state(struct device *dev) return STATE_DISCONNECTED; } + +gboolean device_is_connected(struct device *dev, const char *interface) +{ + if (!interface) { + if ((dev->sink || dev->source) && + avdtp_is_connected(&dev->src, &dev->dst)) + return TRUE; + + if (dev->headset && headset_is_active(dev)) + return TRUE; + } + else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink && + avdtp_is_connected(&dev->src, &dev->dst)) + return TRUE; + else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source && + avdtp_is_connected(&dev->src, &dev->dst)) + return TRUE; + else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset && + headset_is_active(dev)) + return TRUE; + + return FALSE; +} diff --git a/audio/device.h b/audio/device.h index a527c096..a9954314 100644 --- a/audio/device.h +++ b/audio/device.h @@ -76,3 +76,5 @@ int device_remove_stored(struct device *dev); void device_finish_sdp_transaction(struct device *device); uint8_t device_get_state(struct device *dev); + +gboolean device_is_connected(struct device *dev, const char *interface); diff --git a/audio/ipc.h b/audio/ipc.h index 27ee742c..7eb6e398 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -22,6 +22,7 @@ */ #include +#include #define IPC_TYPE_CONNECT 0x0001 @@ -34,10 +35,9 @@ #endif /* Supported roles */ -#define PKT_ROLE_NONE 0 -#define PKT_ROLE_AUTO 1 -#define PKT_ROLE_VOICE 2 -#define PKT_ROLE_HIFI 3 +#define PKT_ROLE_AUTO 0 +#define PKT_ROLE_VOICE 1 +#define PKT_ROLE_HIFI 2 /* Packet types */ #define PKT_TYPE_CFG_REQ 0 @@ -52,7 +52,7 @@ #define PKT_ERROR_NONE 0 struct ipc_packet { - uint8_t id; /* Device id */ + bdaddr_t bdaddr; /* Address of the remote Device */ uint8_t role; /* Audio role eg: voice, wifi, auto... */ uint8_t type; /* Packet type */ uint8_t error; /* Packet error code */ diff --git a/audio/manager.c b/audio/manager.c index c1a72b8f..b918ebba 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -108,19 +108,6 @@ static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, struct audio_sdp_data *data); -static struct device *find_device(bdaddr_t *bda) -{ - GSList *l; - - for (l = devices; l != NULL; l = l->next) { - struct device *device = l->data; - if (bacmp(&device->dst, bda) == 0) - return device; - } - - return NULL; -} - static struct device *create_device(bdaddr_t *bda) { static int device_id = 0; @@ -601,7 +588,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) const char *path; gboolean headset = FALSE, created = FALSE; - device = find_device(bda); + device = manager_find_device(bda, NULL, FALSE); if (!device) { device = create_device(bda); if (!add_device(device)) { @@ -735,7 +722,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, str2ba(address, &bda); - device = find_device(&bda); + device = manager_find_device(&bda, NULL, FALSE); if (!device) { device = create_device(&bda); return resolve_services(msg, device); @@ -870,7 +857,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, const char *param; GSList *l; - default_dev = manager_get_connected_device(); + default_dev = manager_find_device(BDADDR_ANY, NULL, TRUE); if (!default_dev) { l = devices; @@ -930,8 +917,7 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, } str2ba(address, &bda); - - device = find_device(&bda); + device = manager_find_device(&bda, NULL, FALSE); if (!device) return err_does_not_exist(conn, msg); @@ -1069,7 +1055,7 @@ static void parse_stored_devices(char *key, char *value, void *data) return; str2ba(key, &dst); - device = find_device(&dst); + device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); if (device) return; @@ -1093,8 +1079,8 @@ static void register_devices_stored(const char *adapter) struct stat st; struct device *device; bdaddr_t default_src; - bdaddr_t src; bdaddr_t dst; + bdaddr_t src; char *addr; int dev_id; @@ -1114,7 +1100,7 @@ static void register_devices_stored(const char *adapter) dev_id = hci_get_route(&default_src); if (dev_id < 0) return; - + hci_devba(dev_id, &default_src); if (bacmp(&default_src, &src) != 0) return; @@ -1124,7 +1110,7 @@ static void register_devices_stored(const char *adapter) return; str2ba(addr, &dst); - device = find_device(&dst); + device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); if (device) { info("Setting %s as default device", addr); @@ -1760,3 +1746,38 @@ gboolean manager_authorize(bdaddr_t *dba, const char *uuid, return TRUE; } + +struct device *manager_find_device(bdaddr_t *bda, const char *interface, + gboolean connected) +{ + GSList *l; + + if (!bacmp(bda, BDADDR_ANY) && !interface && !connected) + return default_dev; + + for (l = devices; l != NULL; l = l->next) { + struct device *dev = l->data; + + if (bacmp(bda, BDADDR_ANY) && bacmp(&dev->dst, bda)) + continue; + + if (interface && !strcmp(AUDIO_HEADSET_INTERFACE, interface) + && !dev->headset) + continue; + + if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface) + && !dev->sink) + continue; + + if (interface && !strcmp(AUDIO_SOURCE_INTERFACE, interface) + && !dev->source) + continue; + + if (connected && !device_is_connected(dev, interface)) + continue; + + return dev; + } + + return NULL; +} diff --git a/audio/manager.h b/audio/manager.h index 65992040..623a778c 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -42,11 +42,10 @@ void audio_exit(void); uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); int remove_service_record(DBusConnection *conn, uint32_t rec_id); -struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); - -struct device *manager_default_device(); +struct device *manager_find_device(bdaddr_t *bda, const char *interface, + gboolean connected); -struct device *manager_get_connected_device(void); +struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); gboolean manager_authorize(bdaddr_t *dba, const char *uuid, DBusPendingCallNotifyFunction cb, diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index c6df710b..2e5161e9 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -883,7 +883,7 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->sbc.blocks = sbc->blocks; a2dp->sbc.bitpool = sbc->bitpool; a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; + a2dp->sbc.channels * 2; a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); a2dp->pipefd[0] = -1; a2dp->pipefd[1] = -1; @@ -905,12 +905,52 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) struct ipc_packet *pkt = (void *) buf; struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; + snd_config_iterator_t i, next; + const char *addr, *pref; DBG("Sending PKT_TYPE_CFG_REQ..."); memset(buf, 0, sizeof(buf)); + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + + if (snd_config_get_id(n, &id) < 0) + continue; + + printf("id = %s\n", id); + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) + continue; + + if (strcmp(id, "device") == 0) { + if (snd_config_get_string(n, &addr) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + str2ba(addr, &pkt->bdaddr); + continue; + } + + if (strcmp(id, "preference") == 0) { + if (snd_config_get_string(n, &pref) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + else if (!strcmp(pref, "voice") || + !strcmp(pref, "sco")) + pkt->role = PKT_ROLE_VOICE; + else if (!strcmp(pref, "hifi") || + !strcmp(pref, "a2dp")) + pkt->role = PKT_ROLE_HIFI; + continue; + } + + SNDERR("Unknown field %s", id); + return -EINVAL; + } + pkt->type = PKT_TYPE_CFG_REQ; - pkt->role = PKT_ROLE_NONE; pkt->error = PKT_ERROR_NONE; ret = send(data->sock, pkt, sizeof(struct ipc_packet), 0); @@ -969,11 +1009,10 @@ done: data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); if (data->cfg.codec == CFG_CODEC_SBC) { - struct bluetooth_a2dp *a2dp = &data->a2dp; ret = bluetooth_a2dp_init(data, sbc); if (ret < 0) return ret; - printf("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", + DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, a2dp->sbc.bitpool); } diff --git a/audio/unix.c b/audio/unix.c index 3488fd5f..2b9b74f0 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -116,7 +116,7 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) struct iovec iov = { .iov_base = pkt, .iov_len = sizeof(struct ipc_packet) - }; + }; struct msghdr msgh = { .msg_name = 0, @@ -138,18 +138,23 @@ static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) return sendmsg(sock, &msgh, MSG_NOSIGNAL); } -static service_type_t select_service(struct device *dev) +static service_type_t select_service(struct device *dev, const char *interface) { - if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) - return TYPE_SINK; - else if (dev->headset && headset_is_active(dev)) - return TYPE_HEADSET; - else if (dev->sink) + if (!interface) { + if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) + return TYPE_SINK; + else if (dev->headset && headset_is_active(dev)) + return TYPE_HEADSET; + else if (dev->sink) + return TYPE_SINK; + else if (dev->headset) + return TYPE_HEADSET; + } else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink) return TYPE_SINK; - else if (dev->headset) + else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset) return TYPE_HEADSET; - else - return TYPE_NONE; + + return TYPE_NONE; } @@ -215,7 +220,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, } for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { - cap = caps->data; + cap = caps->data; if (cap->category == AVDTP_MEDIA_CODEC) { codec_cap = (void *) cap->data; break; @@ -253,7 +258,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, cfg->codec = CFG_CODEC_SBC; sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? - 0x01 : 0x00; + 0x01 : 0x00; sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; switch (sbc_cap->block_length) { @@ -298,17 +303,23 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int ret, fd; unsigned int id; struct a2dp_data *a2dp; + const char *interface = NULL; + + if (pkt->role == PKT_ROLE_VOICE) + interface = AUDIO_HEADSET_INTERFACE; + else if (pkt->role == PKT_ROLE_HIFI) + interface = AUDIO_SINK_INTERFACE; - dev = manager_get_connected_device(); + dev = manager_find_device(&pkt->bdaddr, interface, TRUE); if (dev) goto proceed; - dev = manager_default_device(); + dev = manager_find_device(&pkt->bdaddr, interface, FALSE); if (!dev) goto failed; proceed: - client->type = select_service(dev); + client->type = select_service(dev, interface); switch (client->type) { case TYPE_SINK: @@ -320,7 +331,7 @@ proceed: if (!a2dp->session) { error("Unable to get a session"); goto failed; - } + } id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, -- cgit From b70de842824ccc09bdf1a0d406bbe1d5cfbed355 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 23 Aug 2007 23:50:33 +0000 Subject: Fix parameter parsing --- audio/pcm_bluetooth.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2e5161e9..0ff1999c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -919,29 +919,32 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) if (snd_config_get_id(n, &id) < 0) continue; - printf("id = %s\n", id); if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) continue; - if (strcmp(id, "device") == 0) { + if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) { if (snd_config_get_string(n, &addr) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } + str2ba(addr, &pkt->bdaddr); continue; } - if (strcmp(id, "preference") == 0) { + if (strcmp(id, "profile") == 0) { if (snd_config_get_string(n, &pref) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - else if (!strcmp(pref, "voice") || - !strcmp(pref, "sco")) + + if (strcmp(pref, "auto") == 0) + pkt->role = PKT_ROLE_AUTO; + else if (strcmp(pref, "voice") == 0 || + strcmp(pref, "hfp") == 0) pkt->role = PKT_ROLE_VOICE; - else if (!strcmp(pref, "hifi") || - !strcmp(pref, "a2dp")) + else if (strcmp(pref, "hifi") == 0 || + strcmp(pref, "a2dp") == 0) pkt->role = PKT_ROLE_HIFI; continue; } -- cgit From 666938b54d631956826343ed278e2af4b982fc29 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Aug 2007 12:15:20 +0000 Subject: Support up to two simultaneous streams --- audio/a2dp.c | 435 +++++++++++++++++++++++++++++++++++----------------------- audio/a2dp.h | 5 +- audio/avdtp.c | 92 ++++++++----- audio/avdtp.h | 50 ++++--- audio/sink.c | 6 +- audio/unix.c | 8 +- 6 files changed, 364 insertions(+), 232 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index febc112c..daeda70c 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -54,12 +54,13 @@ #endif struct a2dp_sep { + uint8_t type; struct avdtp_local_sep *sep; struct avdtp *session; struct avdtp_stream *stream; struct device *used_by; - uint32_t record_id; guint suspend_timer; + gboolean locked; gboolean start_requested; gboolean suspending; gboolean starting; @@ -82,8 +83,11 @@ struct a2dp_stream_setup { static DBusConnection *connection = NULL; -static struct a2dp_sep sink = { NULL }; -static struct a2dp_sep source = { NULL }; +static GSList *sinks = NULL; +static GSList *sources = NULL; + +static uint32_t source_record_id = 0; +static uint32_t sink_record_id = 0; static struct a2dp_stream_setup *setup = NULL; @@ -138,19 +142,16 @@ static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, - uint8_t *category) + uint8_t *category, void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; struct device *dev; bdaddr_t addr; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Set_Configuration_Ind"); - a2dp_sep = &source; - } avdtp_get_peers(session, NULL, &addr); @@ -164,19 +165,20 @@ static gboolean setconf_ind(struct avdtp *session, avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; - if (a2dp_sep == &source) + if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) sink_new_stream(dev, session, stream); return TRUE; } static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, - GSList **caps, uint8_t *err) + GSList **caps, uint8_t *err, void *user_data) { + struct a2dp_sep *a2dp_sep = user_data; struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; - if (sep == sink.sep) + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Get_Capability_Ind"); else debug("SBC Source: Get_Capability_Ind"); @@ -226,18 +228,15 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, - struct avdtp_error *err) + struct avdtp_error *err, void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; int ret; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Set_Configuration_Cfm"); - a2dp_sep = &source; - } if (err) { if (setup) @@ -261,9 +260,11 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err) + uint8_t *err, void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Get_Configuration_Ind"); else debug("SBC Source: Get_Configuration_Ind"); @@ -271,18 +272,24 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Cfm"); else debug("SBC Source: Set_Configuration_Cfm"); } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Ind"); else debug("SBC Source: Open_Ind"); @@ -290,9 +297,12 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Cfm"); else debug("SBC Source: Open_Cfm"); @@ -338,18 +348,15 @@ static gboolean suspend_timeout(struct a2dp_sep *sep) } static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Ind"); - a2dp_sep = &sink; - } - else { + else debug("SBC Source: Start_Ind"); - a2dp_sep = &source; - } a2dp_sep->session = avdtp_ref(session); @@ -360,9 +367,12 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Cfm"); else debug("SBC Source: Start_Cfm"); @@ -384,9 +394,12 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Ind"); else debug("SBC Source: Suspend_Ind"); @@ -394,17 +407,15 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Suspend_Cfm"); - a2dp_sep = &source; - } a2dp_sep->suspending = FALSE; @@ -422,69 +433,63 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Close_Ind"); - a2dp_sep = &source; - } return TRUE; } static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Close_Cfm"); - a2dp_sep = &source; - } } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err) + struct avdtp_stream *stream, uint8_t *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Ind"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Abort_Ind"); - a2dp_sep = &source; - } return TRUE; } static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - struct a2dp_sep *a2dp_sep; + struct a2dp_sep *a2dp_sep = user_data; - if (sep == sink.sep) { + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Cfm"); - a2dp_sep = &sink; - } else { + else debug("SBC Source: Abort_Cfm"); - a2dp_sep = &source; - } } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err) + uint8_t *err, void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Ind"); else debug("SBC Source: ReConfigure_Ind"); @@ -492,9 +497,12 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, } static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err) + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) { - if (sep == sink.sep) + struct a2dp_sep *a2dp_sep = user_data; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); @@ -594,10 +602,62 @@ static int a2dp_sink_record(sdp_buf_t *buf) return 0; } -int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type) { + struct a2dp_sep *sep; + GSList **l; + int (*create_record)(sdp_buf_t *buf); + uint32_t *record_id; sdp_buf_t buf; + sep = g_new0(struct a2dp_sep, 1); + + sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, + &ind, &cfm, sep); + if (sep->sep == NULL) { + g_free(sep); + return NULL; + } + + sep->type = type; + + if (type == AVDTP_SEP_TYPE_SOURCE) { + l = &sources; + create_record = a2dp_source_record; + record_id = &source_record_id; + } else { + l = &sinks; + create_record = a2dp_sink_record; + record_id = &sink_record_id; + } + + if (*record_id != 0) + goto add; + + if (create_record(&buf) < 0) { + error("Unable to allocate new service record"); + avdtp_unregister_sep(sep->sep); + g_free(sep); + return NULL; + } + + *record_id = add_service_record(conn, &buf); + free(buf.data); + if (!*record_id) { + error("Unable to register A2DP service record"); + avdtp_unregister_sep(sep->sep); + g_free(sep); + return NULL; + } + +add: + *l = g_slist_append(*l, sep); + + return sep; +} + +int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +{ if (!enable_sink && !enable_source) return 0; @@ -606,68 +666,40 @@ int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source avdtp_init(); if (enable_sink) { - source.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); - if (source.sep == NULL) - return -1; - - if (a2dp_source_record(&buf) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - source.record_id = add_service_record(conn, &buf); - free(buf.data); - if (!source.record_id) { - error("Unable to register A2DP Source service record"); - return -1; - } + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); } - if (enable_source) { - sink.sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, - AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm); - if (sink.sep == NULL) - return -1; - - if (a2dp_sink_record(&buf) < 0) { - error("Unable to allocate new service record"); - return -1; - } - - sink.record_id = add_service_record(conn, &buf); - free(buf.data); - if (!sink.record_id) { - error("Unable to register A2DP Sink service record"); - return -1; - } - } + if (enable_source) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); return 0; } +static void a2dp_unregister_sep(struct a2dp_sep *sep) +{ + avdtp_unregister_sep(sep->sep); + g_free(sep); +} + void a2dp_exit() { - if (sink.sep) { - avdtp_unregister_sep(sink.sep); - sink.sep = NULL; - } + g_slist_foreach(sinks, (GFunc) a2dp_unregister_sep, NULL); + g_slist_free(sinks); + sinks = NULL; - if (source.sep) { - avdtp_unregister_sep(source.sep); - source.sep = NULL; - } + g_slist_foreach(sources, (GFunc) a2dp_unregister_sep, NULL); + g_slist_free(sources); + sources = NULL; - if (source.record_id) { - remove_service_record(connection, source.record_id); - source.record_id = 0; + if (source_record_id) { + remove_service_record(connection, source_record_id); + source_record_id = 0; } - if (sink.record_id) { - remove_service_record(connection, sink.record_id); - sink.record_id = 0; + if (sink_record_id) { + remove_service_record(connection, sink_record_id); + sink_record_id = 0; } dbus_connection_unref(connection); @@ -878,15 +910,31 @@ gboolean a2dp_source_cancel_stream(int id) } unsigned int a2dp_source_request_stream(struct avdtp *session, - struct device *dev, - gboolean start, a2dp_stream_cb_t cb, - void *user_data) + struct device *dev, + gboolean start, + a2dp_stream_cb_t cb, + void *user_data, + struct a2dp_sep **ret) { struct a2dp_stream_cb *cb_data; static unsigned int cb_id = 0; + GSList *l; + struct a2dp_sep *sep = NULL; - if (source.used_by != NULL && source.used_by != dev) { - error("a2dp_source_request_stream: SEP is locked"); + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (tmp->locked) + continue; + + if (tmp->used_by == NULL || tmp->used_by == dev) { + sep = tmp; + break; + } + } + + if (!sep) { + error("a2dp_source_request_stream: no available SEP found"); return 0; } @@ -896,6 +944,10 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, return 0; } + sep->used_by = dev; + + debug("a2dp_source_request_stream: selected SEP %p", sep); + cb_data = g_new(struct a2dp_stream_cb, 1); cb_data->cb = cb; cb_data->user_data = user_data; @@ -914,9 +966,9 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup->dev = dev; setup->cb = g_slist_append(setup->cb, cb_data); setup->start = start; - setup->stream = source.stream; + setup->stream = sep->stream; - switch (avdtp_sep_get_state(source.sep)) { + switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: if (avdtp_discover(session, discovery_complete, setup) < 0) { error("avdtp_discover failed"); @@ -928,32 +980,35 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, g_idle_add((GSourceFunc) finalize_stream_setup, setup); break; } - if (source.starting) + if (sep->starting) break; - if (avdtp_start(session, source.stream) < 0) { + if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } break; case AVDTP_STATE_STREAMING: - if (!start || !source.suspending) { - if (source.suspend_timer) { - g_source_remove(source.suspend_timer); - source.suspend_timer = 0; + if (!start || !sep->suspending) { + if (sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; } - if (source.session) { - avdtp_unref(source.session); - source.session = NULL; + if (sep->session) { + avdtp_unref(sep->session); + sep->session = NULL; } g_idle_add((GSourceFunc) finalize_stream_setup, setup); return cb_data->id; } - source.start_requested = TRUE; + sep->start_requested = TRUE; break; default: error("SEP in bad state for requesting a new stream"); goto failed; } + + if (ret) + *ret = sep; return cb_data->id; @@ -965,33 +1020,51 @@ failed: gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) { - if (source.used_by) - return FALSE; + GSList *l; - debug("SBC Source locked"); + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *sep = l->data; - source.used_by = dev; + if (sep->locked) + continue; - return TRUE; + debug("SBC Source SEP %p locked", sep); + sep->locked = TRUE; + return TRUE; + } + + return FALSE; } gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (!sep->locked) + continue; + + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } - if (source.used_by != dev) + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); - source.used_by = NULL; + sep->locked = FALSE; + sep->used_by = NULL; - debug("SBC Source unlocked"); + debug("SBC Source SEP %p unlocked", sep); - if (!source.stream || state == AVDTP_STATE_IDLE) + if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; switch (state) { @@ -999,8 +1072,8 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) /* Set timer here */ break; case AVDTP_STATE_STREAMING: - if (avdtp_suspend(session, source.stream) == 0) - source.suspending = TRUE; + if (avdtp_suspend(session, sep->stream) == 0) + sep->suspending = TRUE; break; default: break; @@ -1012,20 +1085,28 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; - if (source.used_by != dev) + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } + + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); - if (!source.stream || state != AVDTP_STATE_STREAMING) + if (!sep->stream || state != AVDTP_STATE_STREAMING) return TRUE; - if (avdtp_suspend(session, source.stream) == 0) { - source.suspending = TRUE; + if (avdtp_suspend(session, sep->stream) == 0) { + sep->suspending = TRUE; return TRUE; } @@ -1035,14 +1116,22 @@ gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) { avdtp_state_t state; + GSList *l; + struct a2dp_sep *sep = NULL; - if (!source.sep) - return FALSE; + for (l = sources; l != NULL; l = l->next) { + struct a2dp_sep *tmp = l->data; + + if (tmp->sep && tmp->used_by == dev) { + sep = tmp; + break; + } + } - if (source.used_by != dev) + if (!sep) return FALSE; - state = avdtp_sep_get_state(source.sep); + state = avdtp_sep_get_state(sep->sep); if (state < AVDTP_STATE_OPEN) { error("a2dp_source_start_stream: no stream open"); @@ -1052,7 +1141,7 @@ gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) if (state == AVDTP_STATE_STREAMING) return TRUE; - if (avdtp_start(session, source.stream) < 0) + if (avdtp_start(session, sep->stream) < 0) return FALSE; return TRUE; diff --git a/audio/a2dp.h b/audio/a2dp.h index 6d69189c..0a838b62 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -58,6 +58,8 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct a2dp_sep; + typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data); @@ -69,7 +71,8 @@ void a2dp_exit(void); unsigned int a2dp_source_request_stream(struct avdtp *session, struct device *dev, gboolean start, a2dp_stream_cb_t cb, - void *user_data); + void *user_data, + struct a2dp_sep **sep); gboolean a2dp_source_cancel_stream(int id); gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); diff --git a/audio/avdtp.c b/audio/avdtp.c index 0b56340f..99a929f4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -219,7 +219,7 @@ struct avdtp_local_sep { GSList *caps; struct avdtp_sep_ind *ind; struct avdtp_sep_cfm *cfm; - void *data; + void *user_data; }; struct stream_callback { @@ -573,7 +573,7 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) g_source_remove(stream->io); if (sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, NULL); + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); } @@ -815,7 +815,8 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, goto failed; } - if (!sep->ind->get_capability(session, sep, &caps, &err)) + if (!sep->ind->get_capability(session, sep, &caps, &err, + sep->user_data)) goto failed; init_response(&rsp->header, &req->header, TRUE); @@ -882,7 +883,8 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, if (sep->ind && sep->ind->set_configuration) { if (!sep->ind->set_configuration(session, sep, stream, stream->caps, &err, - &category)) { + &category, + sep->user_data)) { stream_free(stream); goto failed; } @@ -949,7 +951,8 @@ static gboolean avdtp_open_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->open) { - if (!sep->ind->open(session, sep, stream, &err)) + if (!sep->ind->open(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1009,7 +1012,8 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, } if (sep->ind && sep->ind->start) { - if (!sep->ind->start(session, sep, stream, &err)) + if (!sep->ind->start(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1057,7 +1061,8 @@ static gboolean avdtp_close_cmd(struct avdtp *session, struct seid_req *req, stream = sep->stream; if (sep->ind && sep->ind->close) { - if (!sep->ind->close(session, sep, stream, &err)) + if (!sep->ind->close(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1116,7 +1121,8 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, } if (sep->ind && sep->ind->suspend) { - if (!sep->ind->suspend(session, sep, stream, &err)) + if (!sep->ind->suspend(session, sep, stream, &err, + sep->user_data)) goto failed; } @@ -1156,7 +1162,8 @@ static gboolean avdtp_abort_cmd(struct avdtp *session, struct seid_req *req, } if (sep->ind && sep->ind->abort) { - if (!sep->ind->abort(session, sep, sep->stream, &err)) + if (!sep->ind->abort(session, sep, sep->stream, &err, + sep->user_data)) goto failed; } @@ -1230,7 +1237,8 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, struct avdtp_local_sep *sep = stream->lsep; if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(stream->session, sep, stream, NULL); + sep->cfm->close(stream->session, sep, stream, NULL, + sep->user_data); avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); @@ -1255,7 +1263,7 @@ static void handle_transport_connect(struct avdtp *session, int sock, stream->mtu = mtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, NULL); + sep->cfm->open(session, sep, stream, NULL, sep->user_data); channel = g_io_channel_unix_new(stream->sock); @@ -1556,36 +1564,41 @@ static gboolean request_timeout(gpointer user_data) case AVDTP_RECONFIGURE: error("Reconfigure request timed out"); if (lsep && lsep->cfm && lsep->cfm->reconfigure) - lsep->cfm->reconfigure(session, lsep, stream, &err); + lsep->cfm->reconfigure(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_OPEN: error("Open request timed out"); if (lsep && lsep->cfm && lsep->cfm->open) - lsep->cfm->open(session, lsep, stream, &err); + lsep->cfm->open(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_START: error("Start request timed out"); if (lsep && lsep->cfm && lsep->cfm->start) - lsep->cfm->start(session, lsep, stream, &err); + lsep->cfm->start(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_SUSPEND: error("Suspend request timed out"); if (lsep && lsep->cfm && lsep->cfm->suspend) - lsep->cfm->suspend(session, lsep, stream, &err); + lsep->cfm->suspend(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_CLOSE: error("Close request timed out"); if (lsep && lsep->cfm && lsep->cfm->close) - lsep->cfm->close(session, lsep, stream, &err); + lsep->cfm->close(session, lsep, stream, &err, + lsep->user_data); break; case AVDTP_SET_CONFIGURATION: error("SetConfiguration request timed out"); if (lsep && lsep->cfm && lsep->cfm->set_configuration) - lsep->cfm->set_configuration(session, lsep, stream, &err); - /* fallthrough on purpose */ + lsep->cfm->set_configuration(session, lsep, stream, + &err, lsep->user_data); + goto failed; case AVDTP_DISCOVER: error("Discover request timed out"); - if (lsep && lsep->cfm && lsep->cfm->set_configuration) goto failed; case AVDTP_GET_CAPABILITIES: error("GetCapabilities request timed out"); @@ -1756,7 +1769,8 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, NULL); + sep->cfm->set_configuration(session, sep, stream, NULL, + sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); @@ -1794,7 +1808,7 @@ static gboolean avdtp_start_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream, NULL); + sep->cfm->start(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); @@ -1825,7 +1839,7 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL); + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); return TRUE; } @@ -1837,7 +1851,7 @@ static gboolean avdtp_abort_resp(struct avdtp *session, struct avdtp_local_sep *sep = stream->lsep; if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL); + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -1973,7 +1987,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("OPEN request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, &err); + sep->cfm->open(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_SET_CONFIGURATION: if (!conf_rej_to_err((void *) header, size, &err, &category)) @@ -1981,7 +1996,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("SET_CONFIGURATION request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, &err); + sep->cfm->set_configuration(session, sep, stream, + &err, sep->user_data); return TRUE; case AVDTP_RECONFIGURE: if (!conf_rej_to_err((void *) header, size, &err, &category)) @@ -1989,7 +2005,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("RECONFIGURE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->reconfigure) - sep->cfm->reconfigure(session, sep, stream, &err); + sep->cfm->reconfigure(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_START: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -1997,7 +2014,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("START request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream, &err); + sep->cfm->start(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_SUSPEND: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2005,7 +2023,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("SUSPEND request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, &err); + sep->cfm->suspend(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_CLOSE: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2013,7 +2032,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("CLOSE request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->close) - sep->cfm->close(session, sep, stream, &err); + sep->cfm->close(session, sep, stream, &err, + sep->user_data); return TRUE; case AVDTP_ABORT: if (!stream_rej_to_err((void *) header, size, &err, &acp_seid)) @@ -2021,7 +2041,8 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stre error("ABORT request rejected: %s (%d)", avdtp_strerror(&err), err.err.error_code); if (sep && sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, &err); + sep->cfm->abort(session, sep, stream, &err, + sep->user_data); return TRUE; default: error("Unknown reject response signal id: %u", @@ -2298,6 +2319,9 @@ int avdtp_set_configuration(struct avdtp *session, if (!(lsep && rsep)) return -EINVAL; + debug("avdtp_set_configuration(%p): int_seid=%u, acp_seid=%u", + session, lsep->info.seid, rsep->seid); + new_stream = g_new0(struct avdtp_stream, 1); new_stream->session = session; @@ -2314,8 +2338,8 @@ int avdtp_set_configuration(struct avdtp *session, req = g_malloc0(sizeof(struct setconf_req) + caps_len); init_request(&req->header, AVDTP_SET_CONFIGURATION); - req->acp_seid = lsep->info.seid; - req->int_seid = rsep->seid; + req->int_seid = lsep->info.seid; + req->acp_seid = rsep->seid; /* Copy the capabilities into the request */ for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { @@ -2457,7 +2481,8 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm) + struct avdtp_sep_cfm *cfm, + void *user_data) { struct avdtp_local_sep *sep; @@ -2472,6 +2497,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->info.media_type = media_type; sep->ind = ind; sep->cfm = cfm; + sep->user_data = user_data; local_seps = g_slist_append(local_seps, sep); diff --git a/audio/avdtp.h b/audio/avdtp.h index 49907cff..d759be48 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -97,28 +97,32 @@ struct avdtp_sep_cfm { void (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, + void *user_data); void (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, + void *user_data); void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err); + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err); + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); void (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, - struct avdtp_error *err); + struct avdtp_error *err, void *user_data); }; /* Callbacks for indicating when we received a new command. The return value @@ -126,32 +130,35 @@ struct avdtp_sep_cfm { struct avdtp_sep_ind { gboolean (*get_capability) (struct avdtp *session, struct avdtp_local_sep *sep, - GSList **caps, uint8_t *err); + GSList **caps, uint8_t *err, + void *user_data); gboolean (*set_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, - uint8_t *category); + uint8_t *category, void *user_data); gboolean (*get_configuration) (struct avdtp *session, struct avdtp_local_sep *lsep, - uint8_t *err); + uint8_t *err, void *user_data); gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*suspend) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err); + struct avdtp_stream *stream, uint8_t *err, + void *user_data); gboolean (*reconfigure) (struct avdtp *session, struct avdtp_local_sep *lsep, - uint8_t *err); + uint8_t *err, void *user_data); }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, @@ -200,7 +207,8 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm); + struct avdtp_sep_cfm *cfm, + void *user_data); /* Find a matching pair of local and remote SEP ID's */ int avdtp_get_seps(struct avdtp *session, uint8_t type, uint8_t media, diff --git a/audio/sink.c b/audio/sink.c index 8141eff5..0dc12eb3 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -149,11 +149,13 @@ static void stream_setup_complete(struct avdtp *session, struct device *dev, DBusMessage *reply; reply = dbus_message_new_method_return(pending->msg); send_message_and_unref(pending->conn, reply); + debug("Stream successfully created"); } else { err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; + debug("Stream setup failed"); } pending_request_free(pending); @@ -186,7 +188,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->connect = pending; id = a2dp_source_request_stream(sink->session, dev, FALSE, - stream_setup_complete, pending); + stream_setup_complete, pending, NULL); if (id == 0) { pending_request_free(pending); sink->connect = NULL; @@ -196,6 +198,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, "Failed to request a stream"); } + debug("stream creation in progress"); + pending->id = id; return DBUS_HANDLER_RESULT_HANDLED; diff --git a/audio/unix.c b/audio/unix.c index 2b9b74f0..a04c5bfb 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -61,10 +61,12 @@ typedef void (*notify_cb_t) (struct device *dev, void *data); struct a2dp_data { struct avdtp *session; struct avdtp_stream *stream; + struct a2dp_sep *sep; }; struct unix_client { struct device *dev; + struct avdtp_local_sep *sep; service_type_t type; union { struct a2dp_data a2dp; @@ -335,7 +337,7 @@ proceed: id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, - client); + client, &a2dp->sep); if (id == 0) { error("request_stream failed"); goto failed; @@ -414,7 +416,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) } if (cond & (G_IO_HUP | G_IO_ERR)) { - debug("Unix client disconnected"); + debug("Unix client disconnected (fd=%d)", client->sock); if (!client->dev) goto failed; if (client->disconnect) @@ -488,7 +490,7 @@ static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) return TRUE; } - debug("Accepted new client connection on unix socket"); + debug("Accepted new client connection on unix socket (fd=%d)", cli_sk); client = g_new0(struct unix_client, 1); client->sock = cli_sk; -- cgit From f4211a3cd3442d54fa7877a6c421bee034d46e8e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Aug 2007 12:44:12 +0000 Subject: Fix locking of A2DP SEP's --- audio/a2dp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index daeda70c..9dfa2aeb 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1028,6 +1028,9 @@ gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) if (sep->locked) continue; + if (sep->used_by != dev) + continue; + debug("SBC Source SEP %p locked", sep); sep->locked = TRUE; return TRUE; @@ -1045,7 +1048,7 @@ gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; - if (!sep->locked) + if (!tmp->locked) continue; if (tmp->sep && tmp->used_by == dev) { -- cgit From 44f2f4bacfcb1d7fe694c767446c1c8686e6ccf2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 24 Aug 2007 13:00:09 +0000 Subject: Fix return value when receiving an error. --- audio/pcm_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 0ff1999c..8fb560a4 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -981,7 +981,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) if (pkt->error != PKT_ERROR_NONE) { SNDERR("Error %d while configuring device", pkt->error); - return pkt->error; + return -pkt->error; } if (cfg->codec != CFG_CODEC_SBC) -- cgit From c0de0ea2dce19830f16a731266e4299c60e5b1d7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 24 Aug 2007 13:11:01 +0000 Subject: Remove left-over transport callback in stream_free --- audio/avdtp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 99a929f4..b90c57b8 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -471,6 +471,9 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); + if (stream->io) + g_source_remove(stream->io); + g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); g_slist_free(stream->callbacks); @@ -1240,6 +1243,8 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, sep->cfm->close(stream->session, sep, stream, NULL, sep->user_data); + stream->io = 0; + avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); return FALSE; -- cgit From dbf29a633849e5f59ca097fcc3d810a320f8d16a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 24 Aug 2007 16:55:28 +0000 Subject: Add checks and skeleton for PulseAudio plugin --- audio/Makefile.am | 11 +++++++++++ audio/module-bluetooth-sink.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 audio/module-bluetooth-sink.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index ce16cd12..42fd1590 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -34,6 +34,17 @@ libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ endif +if PULSE +pulsedir = $(libdir)/pulse-0.9/modules + +noinst_LTLIBRARIES = module-bluetooth-sink.la + +module_bluetooth_sink_la_SOURCES = module-bluetooth-sink.c ipc.h +module_bluetooth_sink_la_LDFLAGS = -module -avoid-version +module_bluetooth_sink_la_LIBADD = @SBC_LIBS@ @PULSE_LIBS@ +module_bluetooth_sink_la_CFLAGS = @PULSE_CFLAGS@ @SBC_CFLAGS@ +endif + if GSTREAMER gstreamerdir = $(libdir)/gstreamer-0.10 diff --git a/audio/module-bluetooth-sink.c b/audio/module-bluetooth-sink.c new file mode 100644 index 00000000..96b5d98f --- /dev/null +++ b/audio/module-bluetooth-sink.c @@ -0,0 +1,39 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 + +#if 0 +#include + +PA_MODULE_AUTHOR("Marcel Holtmann ") +PA_MODULE_DESCRIPTION("Bluetooth sink") +PA_MODULE_VERSION(VERSION) + +int pa__init(pa_core *core, pa_module *module) +{ + return 0; +} +#endif -- cgit From a889dc322a49c0bf8f8b77389cfbaa97703e828e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 17:01:57 +0000 Subject: Add SBC stream detection --- audio/gstbluetooth.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'audio') diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 58655573..8c07bae3 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -29,10 +29,31 @@ #include "gstsbcdec.h" #include "gsta2dpsink.h" +static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); + +#define SBC_CAPS (gst_static_caps_get(&sbc_caps)) + +static void sbc_typefind(GstTypeFind *tf, gpointer ignore) +{ + guint8 *data = gst_type_find_peek(tf, 0, 1); + + if (*data != 0x9c) /* SBC syncword */ + return; + + gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, SBC_CAPS); +} + +static gchar *sbc_exts[] = { "sbc", NULL }; + static gboolean plugin_init(GstPlugin *plugin) { GST_INFO("Bluetooth plugin %s", VERSION); + if (gst_type_find_register(plugin, "sbc", + GST_RANK_PRIMARY, sbc_typefind, sbc_exts, + SBC_CAPS, NULL, NULL) == FALSE) + return FALSE; + if (gst_element_register(plugin, "sbcenc", GST_RANK_NONE, GST_TYPE_SBC_ENC) == FALSE) return FALSE; -- cgit From c7826e102e38e12166a038086ce80d3e6645dc3d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 17:03:03 +0000 Subject: Implement full decoding support --- audio/gstsbcdec.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/gstsbcdec.h | 6 +++ 2 files changed, 142 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 9d547eda..ceea4383 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -25,7 +25,7 @@ #include #endif -#include "sbc.h" +#include #include "gstsbcdec.h" @@ -40,21 +40,155 @@ static const GstElementDetails sbc_dec_details = "Decode a SBC audio stream", "Marcel Holtmann "); +static GstStaticPadTemplate sbc_dec_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc")); + +static GstStaticPadTemplate sbc_dec_src_factory = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-raw-int, " + "rate = (int) [ 6000, 48000 ], " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) true, " + "width = (int) 16, " + "depth = (int) 16")); + +static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) +{ + GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad)); + GstFlowReturn res = GST_FLOW_OK; + guint size, offset = 0; + guint8 *data; + GstClockTime timestamp; + + timestamp = GST_BUFFER_TIMESTAMP(buffer); + + if (dec->buffer) { + GstBuffer *temp = buffer; + buffer = gst_buffer_span(dec->buffer, 0, buffer, + GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer)); + gst_buffer_unref(temp); + gst_buffer_unref(dec->buffer); + dec->buffer = NULL; + } + + data = GST_BUFFER_DATA(buffer); + size = GST_BUFFER_SIZE(buffer); + + while (offset < size) { + GstBuffer *output; + GstPadTemplate *template; + GstCaps *caps, *temp; + int consumed; + + consumed = sbc_decode(&dec->sbc, data + offset, size - offset); + if (consumed <= 0) + break; + + caps = gst_caps_new_simple("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sbc.rate, + "channels", G_TYPE_INT, dec->sbc.channels, + NULL); + + template = gst_static_pad_template_get(&sbc_dec_src_factory); + + temp = gst_caps_intersect(caps, + gst_pad_template_get_caps(template)); + + gst_caps_unref(caps); + + res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, + GST_BUFFER_OFFSET_NONE, + dec->sbc.len, temp, &output); + + gst_caps_unref(temp); + + if (res != GST_FLOW_OK) + goto done; + + memcpy(GST_BUFFER_DATA(output), dec->sbc.data, dec->sbc.len); + + res = gst_pad_push(dec->srcpad, output); + if (res != GST_FLOW_OK) + goto done; + + offset += consumed; + } + + if (offset < size) + dec->buffer = gst_buffer_create_sub(buffer, + offset, size - offset); + +done: + gst_buffer_unref(buffer); + gst_object_unref(dec); + + return res; +} + +static GstStateChangeReturn sbc_dec_change_state(GstElement *element, + GstStateChange transition) +{ + GstSbcDec *dec = GST_SBC_DEC(element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG("Setup subband codec"); + if (dec->buffer) { + gst_buffer_unref(dec->buffer); + dec->buffer = NULL; + } + sbc_init(&dec->sbc, 0); + break; + + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG("Finish subband codec"); + if (dec->buffer) { + gst_buffer_unref(dec->buffer); + dec->buffer = NULL; + } + sbc_finish(&dec->sbc); + break; + + default: + break; + } + + return parent_class->change_state(element, transition); +} + static void gst_sbc_dec_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_dec_sink_factory)); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_dec_src_factory)); + gst_element_class_set_details(element_class, &sbc_dec_details); } static void gst_sbc_dec_class_init(GstSbcDecClass *klass) { + GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + gstelement_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state); + GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0, "SBC decoding element"); } -static void gst_sbc_dec_init(GstSbcDec *sbcdec, GstSbcDecClass *klass) +static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) { + self->sinkpad = gst_pad_new_from_static_template(&sbc_dec_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_dec_chain)); + gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template(&sbc_dec_src_factory, "src"); + gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index 0737b9d9..4a6922a0 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -23,6 +23,8 @@ #include +#include "sbc.h" + G_BEGIN_DECLS #define GST_TYPE_SBC_DEC \ @@ -44,6 +46,10 @@ struct _GstSbcDec { GstPad *sinkpad; GstPad *srcpad; + + GstBuffer *buffer; + + sbc_t sbc; }; struct _GstSbcDecClass { -- cgit From 184c50fd834ed54f1f137c65ab8ef461febdeb41 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 17:37:05 +0000 Subject: Limit the supported output rates --- audio/gstsbcdec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index ceea4383..0c7a42e4 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -47,7 +47,7 @@ static GstStaticPadTemplate sbc_dec_sink_factory = static GstStaticPadTemplate sbc_dec_src_factory = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS("audio/x-raw-int, " - "rate = (int) [ 6000, 48000 ], " + "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "endianness = (int) BYTE_ORDER, " "signed = (boolean) true, " -- cgit From 97c120e3f1425d9c431c12167ca4684a0af5f844 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 17:46:57 +0000 Subject: Use device string for the IPC --- audio/Makefile.am | 6 +++--- audio/ipc.h | 3 +-- audio/pcm_bluetooth.c | 9 +++++---- audio/unix.c | 7 +++++-- 4 files changed, 14 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 42fd1590..6d1e75d8 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -25,12 +25,12 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @BLUEZ_LIBS@ @ALSA_LIBS@ -libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @BLUEZ_CFLAGS@ @SBC_CFLAGS@ +libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ +libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic -libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ +libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ endif diff --git a/audio/ipc.h b/audio/ipc.h index 7eb6e398..5279fa2a 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -22,7 +22,6 @@ */ #include -#include #define IPC_TYPE_CONNECT 0x0001 @@ -52,7 +51,7 @@ #define PKT_ERROR_NONE 0 struct ipc_packet { - bdaddr_t bdaddr; /* Address of the remote Device */ + char device[18]; /* Address of the remote Device */ uint8_t role; /* Audio role eg: voice, wifi, auto... */ uint8_t type; /* Packet type */ uint8_t error; /* Packet error code */ diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 8fb560a4..0ddd67ab 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -36,9 +36,6 @@ #include #include -#include -#include - #include "ipc.h" #include "sbc.h" @@ -56,6 +53,10 @@ #define DBG(fmt, arg...) #endif +#ifndef SOL_SCO +#define SOL_SCO 17 +#endif + #ifndef SCO_TXBUFS #define SCO_TXBUFS 0x03 #endif @@ -928,7 +929,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) return -EINVAL; } - str2ba(addr, &pkt->bdaddr); + strncpy(pkt->device, addr, 18); continue; } diff --git a/audio/unix.c b/audio/unix.c index a04c5bfb..246d6813 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -305,18 +305,21 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int ret, fd; unsigned int id; struct a2dp_data *a2dp; + bdaddr_t bdaddr; const char *interface = NULL; + str2ba(pkt->device, &bdaddr); + if (pkt->role == PKT_ROLE_VOICE) interface = AUDIO_HEADSET_INTERFACE; else if (pkt->role == PKT_ROLE_HIFI) interface = AUDIO_SINK_INTERFACE; - dev = manager_find_device(&pkt->bdaddr, interface, TRUE); + dev = manager_find_device(&bdaddr, interface, TRUE); if (dev) goto proceed; - dev = manager_find_device(&pkt->bdaddr, interface, FALSE); + dev = manager_find_device(&bdaddr, interface, FALSE); if (!dev) goto failed; -- cgit From b2906c4c6688ad681ea948f58d1b78b71f8f5622 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 18:05:37 +0000 Subject: Increase the IPC MTU size --- audio/ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 5279fa2a..2043b761 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -25,7 +25,7 @@ #define IPC_TYPE_CONNECT 0x0001 -#define IPC_MTU 32 +#define IPC_MTU 128 #define IPC_SOCKET_NAME "\0/org/bluez/audio" -- cgit From 80c75b2aeb116ae9eb70b864f5dac1891551787d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 18:50:28 +0000 Subject: Fix class variable naming --- audio/gstsbcdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 0c7a42e4..0b50a4ba 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -173,11 +173,11 @@ static void gst_sbc_dec_base_init(gpointer g_class) static void gst_sbc_dec_class_init(GstSbcDecClass *klass) { - GstElementClass *gstelement_class = GST_ELEMENT_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); - gstelement_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state); + element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state); GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0, "SBC decoding element"); -- cgit From d542fb37a81d1d96604d41b36a6d6b11087ba2da Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 25 Aug 2007 19:32:03 +0000 Subject: Implement full encoding support --- audio/gstsbcenc.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/gstsbcenc.h | 4 ++ 2 files changed, 117 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 6e63ac45..86a66270 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -25,7 +25,7 @@ #include #endif -#include "sbc.h" +#include #include "gstsbcenc.h" @@ -40,21 +40,132 @@ static const GstElementDetails sbc_enc_details = "Encode a SBC audio stream", "Marcel Holtmann "); +static GstStaticPadTemplate sbc_enc_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-raw-int, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) true, " + "width = (int) 16, " + "depth = (int) 16")); + +static GstStaticPadTemplate sbc_enc_src_factory = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc")); + +static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) +{ + GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); + GstFlowReturn res = GST_FLOW_OK; + GstStructure *structure; + gint rate, channels; + guint size, offset = 0; + guint8 *data; + + data = GST_BUFFER_DATA(buffer); + size = GST_BUFFER_SIZE(buffer); + + structure = gst_caps_get_structure(GST_PAD_CAPS(pad), 0); + gst_structure_get_int(structure, "rate", &rate); + gst_structure_get_int(structure, "channels", &channels); + + enc->sbc.rate = rate; + enc->sbc.channels = channels; + enc->sbc.subbands = 8; + enc->sbc.joint = 0; + + while (offset < size) { + GstBuffer *output; + GstCaps *caps; + int consumed; + + consumed = sbc_encode(&enc->sbc, data + offset, size - offset); + if (consumed <= 0) + break; + + caps = GST_PAD_CAPS(enc->srcpad); + + res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, + GST_BUFFER_OFFSET_NONE, + enc->sbc.len, caps, &output); + + if (res != GST_FLOW_OK) + goto done; + + memcpy(GST_BUFFER_DATA(output), enc->sbc.data, enc->sbc.len); + + res = gst_pad_push(enc->srcpad, output); + if (res != GST_FLOW_OK) + goto done; + + offset += consumed; + } + + if (offset < size) + res = GST_FLOW_ERROR; + +done: + gst_buffer_unref(buffer); + gst_object_unref(enc); + + return res; +} + +static GstStateChangeReturn sbc_enc_change_state(GstElement *element, + GstStateChange transition) +{ + GstSbcEnc *enc = GST_SBC_ENC(element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG("Setup subband codec"); + sbc_init(&enc->sbc, 0); + break; + + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG("Finish subband codec"); + sbc_finish(&enc->sbc); + break; + + default: + break; + } + + return parent_class->change_state(element, transition); +} + static void gst_sbc_enc_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_enc_sink_factory)); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_enc_src_factory)); + gst_element_class_set_details(element_class, &sbc_enc_details); } static void gst_sbc_enc_class_init(GstSbcEncClass *klass) { + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); + GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, "SBC encoding element"); } -static void gst_sbc_enc_init(GstSbcEnc *sbcenc, GstSbcEncClass *klass) +static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) { + self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink"); + gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index 64457d21..491135c1 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -23,6 +23,8 @@ #include +#include "sbc.h" + G_BEGIN_DECLS #define GST_TYPE_SBC_ENC \ @@ -44,6 +46,8 @@ struct _GstSbcEnc { GstPad *sinkpad; GstPad *srcpad; + + sbc_t sbc; }; struct _GstSbcEncClass { -- cgit From 0551d4b78641ad4cc286d3e69c3268a27f5e9647 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 26 Aug 2007 13:12:47 +0000 Subject: Add mode property to the encoder --- audio/gstsbcenc.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/gstsbcenc.h | 2 ++ 2 files changed, 74 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 86a66270..7f94fc2a 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -32,6 +32,31 @@ GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug +#define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type()) + +static GType gst_sbc_mode_get_type(void) +{ + static GType sbc_mode_type = 0; + static GEnumValue sbc_modes[] = { + { 0, "Auto", "auto" }, + { 1, "Mono", "mono" }, + { 2, "Dual Channel", "dual" }, + { 3, "Stereo", "stereo" }, + { 4, "Joint Stereo", "joint" }, + { -1, NULL, NULL} + }; + + if (!sbc_mode_type) + sbc_mode_type = g_enum_register_static("GstSbcMode", sbc_modes); + + return sbc_mode_type; +} + +enum { + PROP_0, + PROP_MODE, +}; + GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); static const GstElementDetails sbc_enc_details = @@ -52,7 +77,13 @@ static GstStaticPadTemplate sbc_enc_sink_factory = static GstStaticPadTemplate sbc_enc_src_factory = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc")); + GST_STATIC_CAPS("audio/x-sbc, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation = (string) { snr, loudness }")); static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) { @@ -148,14 +179,54 @@ static void gst_sbc_enc_base_init(gpointer g_class) gst_element_class_set_details(element_class, &sbc_enc_details); } +static void gst_sbc_enc_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstSbcEnc *enc = GST_SBC_ENC(object); + + switch (prop_id) { + case PROP_MODE: + enc->mode = g_value_get_enum(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_sbc_enc_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstSbcEnc *enc = GST_SBC_ENC(object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_enum(value, enc->mode); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + static void gst_sbc_enc_class_init(GstSbcEncClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS(klass); GstElementClass *element_class = GST_ELEMENT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); + object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property); + element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); + g_object_class_install_property(object_class, PROP_MODE, + g_param_spec_enum("mode", "Mode", "Encoding mode", + GST_TYPE_SBC_MODE, 0, G_PARAM_READWRITE)); + GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, "SBC encoding element"); } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index 491135c1..e7daf2d3 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -47,6 +47,8 @@ struct _GstSbcEnc { GstPad *sinkpad; GstPad *srcpad; + gint mode; + sbc_t sbc; }; -- cgit From 7e8ae7f5bd23a425674180df138d3dcc3b21afb6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 26 Aug 2007 13:59:05 +0000 Subject: Implement full parsing support --- audio/Makefile.am | 3 +- audio/gstbluetooth.c | 5 ++ audio/gstsbcparse.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++ audio/gstsbcparse.h | 61 ++++++++++++++++ 4 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 audio/gstsbcparse.c create mode 100644 audio/gstsbcparse.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 6d1e75d8..7d23d83a 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -51,8 +51,9 @@ gstreamerdir = $(libdir)/gstreamer-0.10 gstreamer_LTLIBRARIES = libgstbluetooth.la libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ - gstsbcdec.h gstsbcdec.c \ gstsbcenc.h gstsbcenc.c \ + gstsbcdec.h gstsbcdec.c \ + gstsbcparse.h gstsbcparse.c \ gsta2dpsink.h gsta2dpsink.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 8c07bae3..8b2ca76e 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -27,6 +27,7 @@ #include "gstsbcenc.h" #include "gstsbcdec.h" +#include "gstsbcparse.h" #include "gsta2dpsink.h" static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); @@ -62,6 +63,10 @@ static gboolean plugin_init(GstPlugin *plugin) GST_RANK_PRIMARY, GST_TYPE_SBC_DEC) == FALSE) return FALSE; + if (gst_element_register(plugin, "sbcparse", + GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE) + return FALSE; + if (gst_element_register(plugin, "a2dpsink", GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) return FALSE; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c new file mode 100644 index 00000000..5097a85f --- /dev/null +++ b/audio/gstsbcparse.c @@ -0,0 +1,194 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "gstsbcparse.h" + +GST_DEBUG_CATEGORY_STATIC(sbc_parse_debug); +#define GST_CAT_DEFAULT sbc_parse_debug + +GST_BOILERPLATE(GstSbcParse, gst_sbc_parse, GstElement, GST_TYPE_ELEMENT); + +static const GstElementDetails sbc_parse_details = + GST_ELEMENT_DETAILS("Bluetooth SBC parser", + "Codec/Parser/Audio", + "Parse a SBC audio stream", + "Marcel Holtmann "); + +static GstStaticPadTemplate sbc_parse_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc")); + +static GstStaticPadTemplate sbc_parse_src_factory = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation = (string) { snr, loudness }")); + +static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) +{ + GstSbcParse *parse = GST_SBC_PARSE(gst_pad_get_parent(pad)); + GstFlowReturn res = GST_FLOW_OK; + guint size, offset = 0; + guint8 *data; + GstClockTime timestamp; + + timestamp = GST_BUFFER_TIMESTAMP(buffer); + + if (parse->buffer) { + GstBuffer *temp = buffer; + buffer = gst_buffer_span(parse->buffer, 0, buffer, + GST_BUFFER_SIZE(parse->buffer) + GST_BUFFER_SIZE(buffer)); + gst_buffer_unref(temp); + gst_buffer_unref(parse->buffer); + parse->buffer = NULL; + } + + data = GST_BUFFER_DATA(buffer); + size = GST_BUFFER_SIZE(buffer); + + while (offset < size) { + GstBuffer *output; + GstPadTemplate *template; + GstCaps *caps, *temp; + int consumed; + + consumed = sbc_parse(&parse->sbc, data + offset, size - offset); + if (consumed <= 0) + break; + + caps = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, parse->sbc.rate, + "channels", G_TYPE_INT, parse->sbc.channels, + NULL); + + template = gst_static_pad_template_get(&sbc_parse_src_factory); + + temp = gst_caps_intersect(caps, + gst_pad_template_get_caps(template)); + + gst_caps_unref(caps); + + res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad, + GST_BUFFER_OFFSET_NONE, + consumed, temp, &output); + + gst_caps_unref(temp); + + if (res != GST_FLOW_OK) + goto done; + + memcpy(GST_BUFFER_DATA(output), data + offset, consumed); + + res = gst_pad_push(parse->srcpad, output); + if (res != GST_FLOW_OK) + goto done; + + offset += consumed; + } + + if (offset < size) + parse->buffer = gst_buffer_create_sub(buffer, + offset, size - offset); + +done: + gst_buffer_unref(buffer); + gst_object_unref(parse); + + return res; +} + +static GstStateChangeReturn sbc_parse_change_state(GstElement *element, + GstStateChange transition) +{ + GstSbcParse *parse = GST_SBC_PARSE(element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_DEBUG("Setup subband codec"); + if (parse->buffer) { + gst_buffer_unref(parse->buffer); + parse->buffer = NULL; + } + sbc_init(&parse->sbc, 0); + break; + + case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_DEBUG("Finish subband codec"); + if (parse->buffer) { + gst_buffer_unref(parse->buffer); + parse->buffer = NULL; + } + sbc_finish(&parse->sbc); + break; + + default: + break; + } + + return parent_class->change_state(element, transition); +} + +static void gst_sbc_parse_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_parse_sink_factory)); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&sbc_parse_src_factory)); + + gst_element_class_set_details(element_class, &sbc_parse_details); +} + +static void gst_sbc_parse_class_init(GstSbcParseClass *klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + element_class->change_state = GST_DEBUG_FUNCPTR(sbc_parse_change_state); + + GST_DEBUG_CATEGORY_INIT(sbc_parse_debug, "sbcparse", 0, + "SBC parsing element"); +} + +static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) +{ + self->sinkpad = gst_pad_new_from_static_template(&sbc_parse_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain)); + gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template(&sbc_parse_src_factory, "src"); + gst_element_add_pad(GST_ELEMENT(self), self->srcpad); +} diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h new file mode 100644 index 00000000..ceaf2197 --- /dev/null +++ b/audio/gstsbcparse.h @@ -0,0 +1,61 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include "sbc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_SBC_PARSE \ + (gst_sbc_parse_get_type()) +#define GST_SBC_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_PARSE,GstSbcParse)) +#define GST_SBC_PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_PARSE,GstSbcParseClass)) +#define GST_IS_SBC_PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_PARSE)) +#define GST_IS_SBC_PARSE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_PARSE)) + +typedef struct _GstSbcParse GstSbcParse; +typedef struct _GstSbcParseClass GstSbcParseClass; + +struct _GstSbcParse { + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstBuffer *buffer; + + sbc_t sbc; +}; + +struct _GstSbcParseClass { + GstElementClass parent_class; +}; + +GType gst_sbc_parse_get_type(void); + +G_END_DECLS -- cgit From aea33a86fd7cced1796504c2a62f33c7a801a10e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 26 Aug 2007 14:14:34 +0000 Subject: Add possible capabilities and connect to audio server --- audio/gsta2dpsink.c | 169 +++++++++++++++++++++++++++++++++++----------------- audio/gsta2dpsink.h | 4 ++ 2 files changed, 118 insertions(+), 55 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 08832524..8a6a44f1 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -25,14 +25,24 @@ #include #endif +#include +#include +#include + #include "ipc.h" -#include "sbc.h" #include "gsta2dpsink.h" GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug +#define DEFAULT_DEVICE "default" + +enum { + PROP_0, + PROP_DEVICE, +}; + GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstAudioSink, GST_TYPE_AUDIO_SINK); static const GstElementDetails a2dp_sink_details = @@ -41,138 +51,187 @@ static const GstElementDetails a2dp_sink_details = "Plays audio to an A2DP device", "Marcel Holtmann "); -static GstStaticPadTemplate sink_factory = +static GstStaticPadTemplate a2dp_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("ANY")); + GST_STATIC_CAPS("audio/x-sbc, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation = (string) { snr, loudness }; " + + "audio/mpeg, " + "mpegversion = (int) 1, " + "layer = (int) [ 1, 3 ], " + "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ]")); static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - GST_DEBUG(""); - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sink_factory)); + gst_static_pad_template_get(&a2dp_sink_factory)); gst_element_class_set_details(element_class, &a2dp_sink_details); } -static void gst_a2dp_sink_dispose(GObject *object) -{ - G_OBJECT_CLASS(parent_class)->dispose(object); -} - static void gst_a2dp_sink_finalize(GObject *object) { + GstA2dpSink *sink = GST_A2DP_SINK(object); + + g_io_channel_close(sink->server); + + g_free(sink->device); + G_OBJECT_CLASS(parent_class)->finalize(object); } -static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) +static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) { + GstA2dpSink *sink = GST_A2DP_SINK(object); + switch (prop_id) { + case PROP_DEVICE: + g_free(sink->device); + sink->device = g_value_dup_string(value); + + if (sink->device == NULL) + sink->device = g_strdup(DEFAULT_DEVICE); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } -static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) +static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) { + GstA2dpSink *sink = GST_A2DP_SINK(object); + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string(value, sink->device); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } -static GstCaps *gst_a2dp_sink_getcaps(GstBaseSink *basesink) +static gboolean gst_a2dp_sink_open(GstAudioSink *self) { - GST_DEBUG_OBJECT(basesink, ""); + GstA2dpSink *sink = GST_A2DP_SINK(self); - return NULL; -} - -static gboolean gst_a2dp_sink_open(GstAudioSink *audiosink) -{ - GST_DEBUG_OBJECT(audiosink, ""); + printf("device %s\n", sink->device); + printf("open\n"); return TRUE; } -static gboolean gst_a2dp_sink_prepare(GstAudioSink *audiosink, +static gboolean gst_a2dp_sink_prepare(GstAudioSink *self, GstRingBufferSpec *spec) { - GST_DEBUG_OBJECT(audiosink, "spec %p", spec); + printf("perpare\n"); + printf("rate %d\n", spec->rate); + printf("channels %d\n", spec->channels); return TRUE; } -static gboolean gst_a2dp_sink_unprepare(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_unprepare(GstAudioSink *self) { - GST_DEBUG_OBJECT(audiosink, ""); + printf("unprepare\n"); return TRUE; } -static gboolean gst_a2dp_sink_close(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_close(GstAudioSink *self) { - GST_DEBUG_OBJECT(audiosink, ""); + printf("close\n"); return TRUE; } -static guint gst_a2dp_sink_write(GstAudioSink *audiosink, - gpointer data, guint length) +static guint gst_a2dp_sink_write(GstAudioSink *self, + gpointer data, guint length) { - GST_DEBUG_OBJECT(audiosink, "data %p length %d", data, length); - - return length; + return 0; } static guint gst_a2dp_sink_delay(GstAudioSink *audiosink) { - GST_DEBUG_OBJECT(audiosink, ""); + printf("delay\n"); return 0; } static void gst_a2dp_sink_reset(GstAudioSink *audiosink) { - GST_DEBUG_OBJECT(audiosink, ""); + printf("reset\n"); } -static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) +static gboolean server_callback(GIOChannel *chan, + GIOCondition cond, gpointer data) { - GObjectClass *gobject_class = G_OBJECT_CLASS(klass); - GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS(klass); - GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS(klass); + printf("callback\n"); - GST_DEBUG(""); + return TRUE; +} + +static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstAudioSinkClass *audiosink_class = GST_AUDIO_SINK_CLASS(klass); parent_class = g_type_class_peek_parent(klass); - gobject_class->dispose = GST_DEBUG_FUNCPTR(gst_a2dp_sink_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); - gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_property); - gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_property); + object_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); + object_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_property); - gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_getcaps); + audiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dp_sink_open); + audiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_prepare); + audiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unprepare); + audiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dp_sink_close); + audiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dp_sink_write); + audiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dp_sink_delay); + audiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dp_sink_reset); - gstaudiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dp_sink_open); - gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_prepare); - gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unprepare); - gstaudiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dp_sink_close); - gstaudiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dp_sink_write); - gstaudiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dp_sink_delay); - gstaudiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dp_sink_reset); + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device", + DEFAULT_DEVICE, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, "A2DP sink element"); } -static void gst_a2dp_sink_init(GstA2dpSink *a2dpsink, GstA2dpSinkClass *klass) +static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { - GST_DEBUG_OBJECT(a2dpsink, ""); + struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; + int sk; + + self->device = g_strdup(DEFAULT_DEVICE); + + sk = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sk < 0) + return; + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return; + } + + self->server = g_io_channel_unix_new(sk); + + g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + server_callback, self); + + g_io_channel_unref(self->server); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 9575bc44..c83ec66c 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -42,6 +42,10 @@ typedef struct _GstA2dpSinkClass GstA2dpSinkClass; struct _GstA2dpSink { GstAudioSink sink; + + gchar *device; + + GIOChannel *server; }; struct _GstA2dpSinkClass { -- cgit From 66649800c48fc698130d469bf3f38b7361e89e73 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 26 Aug 2007 16:20:14 +0000 Subject: First attempt to make this endian safe --- audio/avdtp.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index b90c57b8..a967be76 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -87,6 +87,8 @@ typedef enum { AVDTP_SESSION_STATE_CONNECTED } avdtp_session_state_t; +#if __BYTE_ORDER == __LITTLE_ENDIAN + struct avdtp_header { uint8_t message_type:2; uint8_t packet_type:2; @@ -109,6 +111,34 @@ struct seid { uint8_t seid:6; } __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t seid:6; + uint8_t inuse:1; + uint8_t rfa0:1; + uint8_t media_type:4; + uint8_t type:1; + uint8_t rfa2:3; +} __attribute__ ((packed)); + +struct seid { + uint8_t seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + /* packets */ struct gen_req { @@ -141,17 +171,25 @@ struct suspend_req { struct seid other_seids[0]; } __attribute__ ((packed)); -struct seid_req { +struct seid_rej { struct avdtp_header header; - uint8_t rfa0:2; - uint8_t acp_seid:6; + uint8_t error; } __attribute__ ((packed)); -struct seid_rej { +struct conf_rej { struct avdtp_header header; + uint8_t category; uint8_t error; } __attribute__ ((packed)); +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct seid_req { + struct avdtp_header header; + uint8_t rfa0:2; + uint8_t acp_seid:6; +} __attribute__ ((packed)); + struct setconf_req { struct avdtp_header header; @@ -163,12 +201,6 @@ struct setconf_req { uint8_t caps[0]; } __attribute__ ((packed)); -struct conf_rej { - struct avdtp_header header; - uint8_t category; - uint8_t error; -} __attribute__ ((packed)); - struct stream_rej { struct avdtp_header header; uint8_t rfa0:2; @@ -195,6 +227,55 @@ struct avdtp_general_rej { uint8_t rfa0; } __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct seid_req { + struct avdtp_header header; + uint8_t acp_seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct setconf_req { + struct avdtp_header header; + + uint8_t int_seid:6; + uint8_t rfa1:2; + uint8_t acp_seid:6; + uint8_t rfa0:2; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct stream_rej { + struct avdtp_header header; + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint8_t error; +} __attribute__ ((packed)); + +struct reconf_req { + struct avdtp_header header; + + uint8_t acp_seid:6; + uint8_t rfa0:2; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct avdtp_general_rej { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + struct pending_req { struct avdtp_header *msg; int msg_size; -- cgit From ca3ff4f6a27ff2d421a7f8fdd3bd5dd67f1f47df Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 09:06:07 +0000 Subject: Fix headset state change when an error occurs on both the SCO and the RFCOMM fd's in the same mainloop iteration --- audio/headset.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 056f9e18..5c758db0 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -86,6 +86,7 @@ struct headset { GIOChannel *rfcomm; GIOChannel *sco; + guint sco_id; guint ring_timer; @@ -188,6 +189,8 @@ static void close_sco(struct device *device) struct headset *hs = device->headset; if (hs->sco) { + g_source_remove(hs->sco_id); + hs->sco_id = 0; g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); hs->sco = NULL; @@ -1523,8 +1526,9 @@ void headset_set_state(struct device *dev, headset_state_t state) case HEADSET_STATE_PLAY_IN_PROGRESS: break; case HEADSET_STATE_PLAYING: - g_io_add_watch(hs->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) sco_cb, dev); + hs->sco_id = g_io_add_watch(hs->sco, + G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) sco_cb, dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, -- cgit From e1bfc91de96a38616100cc31db7bdb69f2cfbea6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 11:31:01 +0000 Subject: Convert alsa initiated headset connections to similar callback system that A2DP is already using --- audio/headset.c | 98 ++++++++++++++++++----------------- audio/headset.h | 8 ++- audio/main.c | 1 - audio/sink.c | 2 - audio/unix.c | 156 ++++++++++++++++++++++++++++++++------------------------ audio/unix.h | 2 - 6 files changed, 145 insertions(+), 122 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 5c758db0..da224b8a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -51,11 +51,9 @@ #include "dbus.h" #include "dbus-helper.h" #include "logging.h" -#include "ipc.h" #include "device.h" #include "manager.h" #include "error.h" -#include "unix.h" #include "headset.h" #define RING_INTERVAL 3000 @@ -71,11 +69,12 @@ static char *str_state[] = {"DISCONNECTED", "CONNECTING", "CONNECTED", struct pending_connect { DBusMessage *msg; GIOChannel *io; - struct ipc_packet *pkt; - int pkt_len; guint io_id; int sock; int err; + unsigned int id; + headset_stream_cb_t cb; + void *cb_data; }; struct headset { @@ -109,8 +108,6 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c); static void pending_connect_free(struct pending_connect *c) { - if (c->pkt) - g_free(c->pkt); if (c->io) { g_io_channel_close(c->io); g_io_channel_unref(c->io); @@ -342,6 +339,7 @@ static GIOError headset_send(struct headset *hs, const char *str) static void pending_connect_ok(struct pending_connect *c, struct device *dev) { + struct headset *hs = dev->headset; DBusMessage *reply; if (c->msg) { @@ -349,29 +347,34 @@ static void pending_connect_ok(struct pending_connect *c, struct device *dev) if (reply) send_message_and_unref(dev->conn, reply); } - else if (c->pkt) { - struct ipc_data_cfg *rsp; - int ret, fd; - - ret = headset_get_config(dev, c->sock, c->pkt, c->pkt_len, - &rsp, &fd); - if (ret == 0) { - unix_send_cfg(c->sock, rsp, fd); - g_free(rsp); - } + + if (c->cb) { + if (hs->rfcomm && hs->sco) + c->cb(dev, c->cb_data); else - unix_send_cfg(c->sock, NULL, -1); + c->cb(NULL, c->cb_data); } pending_connect_free(c); } +static gboolean finalize_stream_setup(struct device *dev) +{ + struct headset *hs = dev->headset; + + g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, dev); + g_slist_free(hs->pending); + hs->pending = NULL; + + return FALSE; +} + static void pending_connect_failed(struct pending_connect *c, struct device *dev) { if (c->msg) err_connect_failed(dev->conn, c->msg, strerror(c->err)); - if (c->pkt) - unix_send_cfg(c->sock, NULL, -1); + if (c->cb) + c->cb(NULL, c->cb_data); pending_connect_free(c); } @@ -537,7 +540,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, (GIOFunc) rfcomm_io_cb, device); - if (c->pkt) { + if (c->cb) { if (sco_connect(device, c) < 0) goto failed; return FALSE; @@ -1396,50 +1399,38 @@ void headset_free(struct device *dev) dev->headset = NULL; } -int headset_get_config(struct device *dev, int sock, struct ipc_packet *pkt, - int pkt_len, struct ipc_data_cfg **cfg, int *fd) +unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, + void *user_data) { struct headset *hs = dev->headset; - int err = EINVAL; struct pending_connect *c; - - if (hs->rfcomm && hs->sco) - goto proceed; + static unsigned int cb_id = 0; + int err; c = g_new0(struct pending_connect, 1); - c->sock = sock; - c->pkt = g_malloc(pkt_len); - memcpy(c->pkt, pkt, pkt_len); + c->cb = cb; + c->cb_data = user_data; + c->id = ++cb_id; + + if (hs->rfcomm && hs->sco) { + hs->pending = g_slist_append(hs->pending, c); + g_idle_add((GSourceFunc) finalize_stream_setup, hs); + return c->id; + } if (hs->rfcomm == NULL) err = rfcomm_connect(dev, c); else if (hs->sco == NULL) err = sco_connect(dev, c); - else - goto error; if (err < 0) goto error; - return 1; - -proceed: - *cfg = g_new0(struct ipc_data_cfg, 1); - (*cfg)->fd_opt = CFG_FD_OPT_READWRITE; - (*cfg)->codec = CFG_CODEC_NONE; - (*cfg)->channels = 1; - (*cfg)->channel_mode = CFG_CHANNEL_MODE_MONO; - (*cfg)->pkt_len = 48; - (*cfg)->sample_size = 2; - (*cfg)->rate = 8000; - *fd = g_io_channel_unix_get_fd(hs->sco); - - return 0; + return c->id; error: - if (c) - pending_connect_free(c); - return -err; + pending_connect_free(c); + return 0; } headset_type_t headset_get_type(struct device *dev) @@ -1607,3 +1598,14 @@ gboolean headset_play(struct device *dev, void *data) { return TRUE; } + +int headset_get_sco_fd(struct device *dev) +{ + struct headset *hs = dev->headset; + + if (!hs->sco) + return -1; + + return g_io_channel_unix_get_fd(hs->sco); +} + diff --git a/audio/headset.h b/audio/headset.h index 29e2e496..31486834 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -46,6 +46,8 @@ typedef enum { SVC_HANDSFREE } headset_type_t; +typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data); + struct headset *headset_init(struct device *dev, sdp_record_t *record, uint16_t svc); @@ -53,8 +55,8 @@ void headset_free(struct device *dev); void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); -int headset_get_config(struct device *dev, int sock, struct ipc_packet *pkt, - int pkt_len, struct ipc_data_cfg **rsp, int *fd); +unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, + void *user_data); headset_type_t headset_get_type(struct device *dev); void headset_set_type(struct device *dev, headset_type_t type); @@ -67,6 +69,8 @@ void headset_set_state(struct device *dev, headset_state_t state); int headset_get_channel(struct device *dev); +int headset_get_sco_fd(struct device *dev); + gboolean headset_is_active(struct device *dev); gboolean headset_lock(struct device *dev, void *data); diff --git a/audio/main.c b/audio/main.c index bd9cd0c5..3ca2ba40 100644 --- a/audio/main.c +++ b/audio/main.c @@ -37,7 +37,6 @@ #include "dbus.h" #include "logging.h" -#include "ipc.h" #include "unix.h" #include "manager.h" diff --git a/audio/sink.c b/audio/sink.c index 0dc12eb3..4bebf738 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -38,11 +38,9 @@ #include "logging.h" #include "avdtp.h" -#include "ipc.h" #include "device.h" #include "a2dp.h" #include "error.h" -#include "unix.h" #include "sink.h" struct pending_request { diff --git a/audio/unix.c b/audio/unix.c index 246d6813..9fa5fc08 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -185,6 +185,86 @@ static void stream_state_changed(struct avdtp_stream *stream, } } +static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) +{ + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + int len, codec_len; + + memset(buf, 0, sizeof(buf)); + + pkt->type = PKT_TYPE_CFG_RSP; + + if (!cfg) { + pkt->error = EINVAL; + len = send(sock, pkt, sizeof(struct ipc_packet), 0); + if (len < 0) + error("send: %s (%d)", strerror(errno), errno); + return len; + } + + debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u," + "sample_size=%u, rate=%u", fd, cfg->fd_opt, cfg->channels, + cfg->pkt_len, cfg->sample_size, cfg->rate); + + if (cfg->codec == CFG_CODEC_SBC) + codec_len = sizeof(struct ipc_codec_sbc); + else + codec_len = 0; + + pkt->error = PKT_ERROR_NONE; + pkt->length = sizeof(struct ipc_data_cfg) + codec_len; + memcpy(pkt->data, cfg, pkt->length); + + len = sizeof(struct ipc_packet) + pkt->length; + len = send(sock, pkt, len, 0); + if (len < 0) + error("Error %s(%d)", strerror(errno), errno); + + debug("%d bytes sent", len); + + if (fd != -1) { + len = unix_sendmsg_fd(sock, fd, pkt); + if (len < 0) + error("Error %s(%d)", strerror(errno), errno); + debug("%d bytes sent", len); + } + + return 0; +} + + +static void headset_setup_complete(struct device *dev, void *user_data) +{ + struct unix_client *client = user_data; + struct ipc_data_cfg cfg; + int fd; + + if (!dev) { + unix_send_cfg(client->sock, NULL, -1); + client->dev = NULL; + return; + } + + memset(&cfg, 0, sizeof(cfg)); + + cfg.fd_opt = CFG_FD_OPT_READWRITE; + cfg.codec = CFG_CODEC_NONE; + cfg.channels = 1; + cfg.channel_mode = CFG_CHANNEL_MODE_MONO; + cfg.pkt_len = 48; + cfg.sample_size = 2; + cfg.rate = 8000; + + fd = headset_get_sco_fd(dev); + + unix_send_cfg(client->sock, &cfg, fd); + + client->disconnect = (notify_cb_t) headset_unlock; + client->suspend = (notify_cb_t) headset_suspend; + client->play = (notify_cb_t) headset_play; +} + static void a2dp_setup_complete(struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data) @@ -300,9 +380,7 @@ failed: static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) { - struct ipc_data_cfg *rsp; struct device *dev; - int ret, fd; unsigned int id; struct a2dp_data *a2dp; bdaddr_t bdaddr; @@ -341,32 +419,24 @@ proceed: id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client, &a2dp->sep); - if (id == 0) { - error("request_stream failed"); - goto failed; - } - - client->req_id = id; - break; case TYPE_HEADSET: - if (!headset_lock(dev, client->d.data)) { - error("Unable to lock headset"); - goto failed; - } - - ret = headset_get_config(dev, client->sock, pkt, len, &rsp, - &fd); - client->disconnect = (notify_cb_t) headset_unlock; - client->suspend = (notify_cb_t) headset_suspend; - client->play = (notify_cb_t) headset_play; + id = headset_request_stream(dev, headset_setup_complete, + client); break; default: error("No known services for device"); goto failed; } + if (id == 0) { + error("request_stream failed"); + goto failed; + } + + client->req_id = id; client->dev = dev; + return; failed: @@ -553,54 +623,6 @@ void unix_exit(void) unix_sock = -1; } -int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) -{ - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - int len, codec_len; - - memset(buf, 0, sizeof(buf)); - - pkt->type = PKT_TYPE_CFG_RSP; - - if (!cfg) { - pkt->error = EINVAL; - len = send(sock, pkt, sizeof(struct ipc_packet), 0); - if (len < 0) - error("send: %s (%d)", strerror(errno), errno); - return len; - } - - debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u," - "sample_size=%u, rate=%u", fd, cfg->fd_opt, cfg->channels, - cfg->pkt_len, cfg->sample_size, cfg->rate); - - if (cfg->codec == CFG_CODEC_SBC) - codec_len = sizeof(struct ipc_codec_sbc); - else - codec_len = 0; - - pkt->error = PKT_ERROR_NONE; - pkt->length = sizeof(struct ipc_data_cfg) + codec_len; - memcpy(pkt->data, cfg, pkt->length); - - len = sizeof(struct ipc_packet) + pkt->length; - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - - debug("%d bytes sent", len); - - if (fd != -1) { - len = unix_sendmsg_fd(sock, fd, pkt); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - debug("%d bytes sent", len); - } - - return 0; -} - #if 0 static int unix_send_state(int sock, struct ipc_packet *pkt) { diff --git a/audio/unix.h b/audio/unix.h index 7f42688c..feea855f 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -23,5 +23,3 @@ int unix_init(void); void unix_exit(void); - -int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd); -- cgit From 296dcf42cf8a4f4d6f0192cac58a28887be38552 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 12:05:10 +0000 Subject: First try at device autocreate support --- audio/a2dp.c | 2 +- audio/main.c | 1 + audio/manager.c | 31 +++++++++++++++++++---- audio/manager.h | 5 ++++ audio/unix.c | 77 ++++++++++++++++++++++++++++++++++++++++----------------- 5 files changed, 87 insertions(+), 29 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 9dfa2aeb..d3cedefc 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -35,8 +35,8 @@ #include #include "logging.h" -#include "manager.h" #include "device.h" +#include "manager.h" #include "avdtp.h" #include "sink.h" #include "a2dp.h" diff --git a/audio/main.c b/audio/main.c index 3ca2ba40..ae43f170 100644 --- a/audio/main.c +++ b/audio/main.c @@ -38,6 +38,7 @@ #include "dbus.h" #include "logging.h" #include "unix.h" +#include "device.h" #include "manager.h" static gboolean disable_hfp = TRUE; diff --git a/audio/manager.c b/audio/manager.c index b918ebba..ac4528f7 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -87,6 +87,9 @@ struct audio_sdp_data { GSList *records; /* sdp_record_t * */ audio_sdp_state_t state; + + create_dev_cb_t cb; + void *cb_data; }; static DBusConnection *connection = NULL; @@ -334,8 +337,13 @@ update: } done: - if (!success) + if (success) { + if (data->cb) + data->cb(data->device, data->cb_data); + } else { remove_device(data->device); + data->cb(NULL, data->cb_data); + } if (data->msg) dbus_message_unref(data->msg); g_slist_foreach(data->handles, (GFunc) g_free, NULL); @@ -570,7 +578,9 @@ failed: } static DBusHandlerResult resolve_services(DBusMessage *msg, - struct device *device) + struct device *device, + create_dev_cb_t cb, + void *user_data) { struct audio_sdp_data *sdp_data; @@ -578,6 +588,8 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, if (msg) sdp_data->msg = dbus_message_ref(msg); sdp_data->device = device; + sdp_data->cb = cb; + sdp_data->cb_data = user_data; return get_handles(GENERIC_AUDIO_UUID, sdp_data); } @@ -628,7 +640,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - resolve_services(NULL, device); + resolve_services(NULL, device, NULL, NULL); } if (headset) @@ -699,6 +711,15 @@ static gboolean device_matches(struct device *device, char **interfaces) return TRUE; } +void manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, + void *user_data) +{ + struct device *dev; + + dev = create_device(bda); + resolve_services(NULL, dev, cb, user_data); +} + static DBusHandlerResult am_create_device(DBusConnection *conn, DBusMessage *msg, void *data) @@ -725,7 +746,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, device = manager_find_device(&bda, NULL, FALSE); if (!device) { device = create_device(&bda); - return resolve_services(msg, device); + return resolve_services(msg, device, NULL, NULL); } path = device->path; @@ -849,8 +870,8 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, return DBUS_HANDLER_RESULT_NEED_MEMORY; device = match->data; - remove_device(device); device_remove_stored(device); + remove_device(device); /* Fallback to a valid default */ if (default_dev == NULL) { diff --git a/audio/manager.h b/audio/manager.h index 623a778c..42632d17 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -34,6 +34,8 @@ struct enabled_interfaces { gboolean target; }; +typedef void (*create_dev_cb_t) (struct device *dev, void *user_data); + int audio_init(DBusConnection *conn, struct enabled_interfaces *enabled, gboolean no_hfp, gboolean sco_hci); @@ -47,6 +49,9 @@ struct device *manager_find_device(bdaddr_t *bda, const char *interface, struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); +void manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, + void *user_data); + gboolean manager_authorize(bdaddr_t *dba, const char *uuid, DBusPendingCallNotifyFunction cb, void *user_data, diff --git a/audio/unix.c b/audio/unix.c index 9fa5fc08..1d1a0b6a 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -68,6 +68,7 @@ struct unix_client { struct device *dev; struct avdtp_local_sep *sep; service_type_t type; + char *interface; union { struct a2dp_data a2dp; void *data; @@ -104,6 +105,7 @@ static void client_free(struct unix_client *client) if (client->sock >= 0) close(client->sock); + g_free(client->interface); g_free(client); } @@ -377,32 +379,12 @@ failed: a2dp->stream = NULL; } -static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, - int len) +static void create_stream(struct device *dev, struct unix_client *client) { - struct device *dev; - unsigned int id; struct a2dp_data *a2dp; - bdaddr_t bdaddr; - const char *interface = NULL; - - str2ba(pkt->device, &bdaddr); - - if (pkt->role == PKT_ROLE_VOICE) - interface = AUDIO_HEADSET_INTERFACE; - else if (pkt->role == PKT_ROLE_HIFI) - interface = AUDIO_SINK_INTERFACE; - - dev = manager_find_device(&bdaddr, interface, TRUE); - if (dev) - goto proceed; - - dev = manager_find_device(&bdaddr, interface, FALSE); - if (!dev) - goto failed; + unsigned int id; -proceed: - client->type = select_service(dev, interface); + client->type = select_service(dev, client->interface); switch (client->type) { case TYPE_SINK: @@ -443,6 +425,55 @@ failed: unix_send_cfg(client->sock, NULL, -1); } +static void create_cb(struct device *dev, void *user_data) +{ + struct unix_client *client = user_data; + + if (!dev) + unix_send_cfg(client->sock, NULL, -1); + else + create_stream(dev, client); +} + +static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, + int len) +{ + struct device *dev; + bdaddr_t bdaddr; + + str2ba(pkt->device, &bdaddr); + + if (client->interface) { + g_free(client->interface); + client->interface = NULL; + } + + if (pkt->role == PKT_ROLE_VOICE) + client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); + else if (pkt->role == PKT_ROLE_HIFI) + client->interface = g_strdup(AUDIO_SINK_INTERFACE); + + if (!manager_find_device(&bdaddr, NULL, FALSE)) { + if (!bacmp(&bdaddr, BDADDR_ANY)) + goto failed; + manager_create_device(&bdaddr, create_cb, client); + return; + } + + dev = manager_find_device(&bdaddr, client->interface, TRUE); + if (!dev) + dev = manager_find_device(&bdaddr, client->interface, FALSE); + + if (!dev) + goto failed; + + create_stream(dev, client); + return; + +failed: + unix_send_cfg(client->sock, NULL, -1); +} + static void ctl_event(struct unix_client *client, struct ipc_packet *pkt, int len) { -- cgit From 8a4b3eedbb21fc9a88b3a3d2be42b2369b969f02 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 13:28:14 +0000 Subject: Change ListDevices not to require any parameters --- audio/audio-api.txt | 5 ++-- audio/manager.c | 80 +++++------------------------------------------------ 2 files changed, 9 insertions(+), 76 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 6a4041c0..c49b61b9 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -31,11 +31,10 @@ Methods Removes a device from the device tree. If there are any connections open to the device they will be closed. - array{string} ListDevices(array{string} interfaces) [experimental] + array{string} ListDevices() [experimental] Retuns an array of strings indicating the object paths - of available devices which implement as a minimum the - interfaces given through the parameter. + of available devices. string DefaultDevice() diff --git a/audio/manager.c b/audio/manager.c index ac4528f7..5df00c2f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -671,46 +671,6 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) return device; } -static gboolean device_supports_interface(struct device *device, - const char *iface) -{ - if (strcmp(iface, AUDIO_HEADSET_INTERFACE) == 0) - return device->headset ? TRUE : FALSE; - - if (strcmp(iface, AUDIO_GATEWAY_INTERFACE) == 0) - return device->gateway ? TRUE : FALSE; - - if (strcmp(iface, AUDIO_SOURCE_INTERFACE) == 0) - return device->source ? TRUE : FALSE; - - if (strcmp(iface, AUDIO_SINK_INTERFACE) == 0) - return device->sink ? TRUE : FALSE; - - if (strcmp(iface, AUDIO_CONTROL_INTERFACE) == 0) - return device->control ? TRUE : FALSE; - - if (strcmp(iface, AUDIO_TARGET_INTERFACE) == 0) - return device->target ? TRUE : FALSE; - - debug("Unknown interface %s", iface); - - return FALSE; -} - -static gboolean device_matches(struct device *device, char **interfaces) -{ - int i; - - for (i = 0; interfaces[i]; i++) { - if (device_supports_interface(device, interfaces[i])) - continue; - debug("Device does not support interface %s", interfaces[i]); - return FALSE; - } - - return TRUE; -} - void manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data) { @@ -769,40 +729,16 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, DBusMessage *reply; DBusError derr; GSList *l; - char **required; - int required_len; + gboolean hs_only = FALSE; + dbus_error_init(&derr); if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, - "ListHeadsets")) { - required = dbus_new0(char *, 2); - if (required == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - required[0] = dbus_new0(char, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - if (required[0] == NULL) { - dbus_free(required); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - memcpy(required[0], AUDIO_HEADSET_INTERFACE, - strlen(AUDIO_HEADSET_INTERFACE) + 1); - required[1] = NULL; - required_len = 1; - } + "ListHeadsets")) + hs_only = TRUE; else - dbus_message_get_args(msg, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &required, &required_len, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + hs_only = FALSE; reply = dbus_message_new_method_return(msg); if (!reply) @@ -816,7 +752,7 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, for (l = devices; l != NULL; l = l->next) { struct device *device = l->data; - if (!device_matches(device, required)) + if (hs_only && !device->headset) continue; dbus_message_iter_append_basic(&array_iter, @@ -825,8 +761,6 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, dbus_message_iter_close_container(&iter, &array_iter); - dbus_free_string_array(required); - return send_message_and_unref(connection, reply); } @@ -1037,7 +971,7 @@ static DBusMethodVTable manager_methods[] = { { "RemoveDevice", am_remove_device, "s", "" }, { "ListDevices", am_list_devices, - "as", "as" }, + "", "as" }, { "DefaultDevice", am_default_device, "", "s" }, { "ChangeDefaultDevice", am_change_default_device, -- cgit From 2bb0572a896151e0cf838282fb81372fa6c47999 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 27 Aug 2007 14:10:00 +0000 Subject: Force LITTLE_ENDIAN instead of BYTE_ORDER for now --- audio/gstsbcdec.c | 2 +- audio/gstsbcenc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 0b50a4ba..98504d4d 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -49,7 +49,7 @@ static GstStaticPadTemplate sbc_dec_src_factory = GST_STATIC_CAPS("audio/x-raw-int, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " + "endianness = (int) LITTLE_ENDIAN, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")); diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 7f94fc2a..812c1193 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -70,7 +70,7 @@ static GstStaticPadTemplate sbc_enc_sink_factory = GST_STATIC_CAPS("audio/x-raw-int, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " + "endianness = (int) LITTLE_ENDIAN, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")); -- cgit From cb379b6a76ad441dc427e86bd17b47014a85fb7a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 14:17:39 +0000 Subject: Fix removing last device --- audio/manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 5df00c2f..ae832f1c 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -814,7 +814,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, default_dev = manager_find_device(BDADDR_ANY, NULL, TRUE); - if (!default_dev) { + if (!default_dev && devices) { l = devices; default_dev = (g_slist_last(l))->data; } -- cgit From a5189990ba7e0173b89fbdc115fb376d690d6f9c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 14:36:05 +0000 Subject: Fix DeviceCreated signal sending --- audio/manager.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index ae832f1c..7e29a9ea 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -141,6 +141,20 @@ static void remove_device(struct device *device) static gboolean add_device(struct device *device) { + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "DeviceCreated", + DBUS_TYPE_STRING, &device->path, + DBUS_TYPE_INVALID); + + if (device->headset) + dbus_connection_emit_signal(connection, + AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + "HeadsetCreated", + DBUS_TYPE_STRING, &device->path, + DBUS_TYPE_INVALID); + if (default_dev == NULL && g_slist_length(devices) == 0) { debug("Selecting default device"); default_dev = device; @@ -310,26 +324,13 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) goto done; } - add_device(data->device); - update: g_slist_foreach(data->records, (GFunc) handle_record, data->device); + if (!g_slist_find(devices, data->device)) + add_device(data->device); + if (reply) { - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "DeviceCreated", - DBUS_TYPE_STRING, - &data->device->path, - DBUS_TYPE_INVALID); - if (data->device->headset) - dbus_connection_emit_signal(connection, - AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - "HeadsetCreated", - DBUS_TYPE_STRING, - &data->device->path, - DBUS_TYPE_INVALID); dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->device->path, DBUS_TYPE_INVALID); -- cgit From 4a4d0dbfe2c45c2ed4391004767a862bd4008370 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 27 Aug 2007 14:47:51 +0000 Subject: Don't send DeviceCreated signals on service start --- audio/manager.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 7e29a9ea..1228b2cc 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -139,8 +139,11 @@ static void remove_device(struct device *device) dbus_connection_destroy_object_path(connection, device->path); } -static gboolean add_device(struct device *device) +static gboolean add_device(struct device *device, gboolean send_signals) { + if (!send_signals) + goto add; + dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", @@ -154,6 +157,7 @@ static gboolean add_device(struct device *device) "HeadsetCreated", DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); +add: if (default_dev == NULL && g_slist_length(devices) == 0) { debug("Selecting default device"); @@ -328,7 +332,7 @@ update: g_slist_foreach(data->records, (GFunc) handle_record, data->device); if (!g_slist_find(devices, data->device)) - add_device(data->device); + add_device(data->device, TRUE); if (reply) { dbus_message_append_args(reply, DBUS_TYPE_STRING, @@ -604,7 +608,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) device = manager_find_device(bda, NULL, FALSE); if (!device) { device = create_device(bda); - if (!add_device(device)) { + if (!add_device(device, TRUE)) { remove_device(device); return NULL; } @@ -1026,7 +1030,7 @@ static void parse_stored_devices(char *key, char *value, void *data) device->headset = headset_init(device, NULL, 0); if (strstr(value, "sink")) device->sink = sink_init(device); - add_device(device); + add_device(device, FALSE); } static void register_devices_stored(const char *adapter) -- cgit From 892a1edab753356dba7c0b5373e414ea71fcf6fa Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 27 Aug 2007 21:45:03 +0000 Subject: Fix alsa plugin for hsp devices. --- audio/pcm_bluetooth.c | 120 +++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 0ddd67ab..e0264018 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -98,10 +98,6 @@ struct bluetooth_a2dp { int nsamples; /* Cumulative number of codec samples */ uint16_t seq_num; /* Cumulative packet sequence */ int frame_count; /* Current frames in buffer*/ - - pthread_t hw_thread; /* Makes virtual hw pointer move */ - int pipefd[2]; /* Inter thread communication */ - int stopped; }; struct bluetooth_data { @@ -113,6 +109,10 @@ struct bluetooth_data { uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ + + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ + int stopped; }; static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -129,10 +129,9 @@ static int bluetooth_stop(snd_pcm_ioplug_t *io) return 0; } -static void *a2dp_playback_hw_thread(void *param) +static void *playback_hw_thread(void *param) { struct bluetooth_data *data = param; - struct bluetooth_a2dp *a2dp = &data->a2dp; unsigned int prev_periods; double period_time; struct timeval start; @@ -146,7 +145,7 @@ static void *a2dp_playback_hw_thread(void *param) unsigned int dtime, periods; struct timeval cur, delta; - if (a2dp->stopped) + if (data->stopped) goto iter_sleep; gettimeofday(&cur, 0); @@ -164,7 +163,7 @@ static void *a2dp_playback_hw_thread(void *param) data->hw_ptr %= data->io.buffer_size; /* Notify user that hardware pointer has moved */ - if (write(a2dp->pipefd[1], &c, 1) < 0) + if (write(data->pipefd[1], &c, 1) < 0) pthread_testcancel(); /* Reset point of reference to avoid too big values @@ -185,33 +184,30 @@ iter_sleep: } } -static int bluetooth_a2dp_playback_start(snd_pcm_ioplug_t *io) +static int bluetooth_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; int err; DBG("%p", io); - a2dp->stopped = 0; + data->stopped = 0; - if (a2dp->hw_thread) + if (data->hw_thread) return 0; - err = pthread_create(&a2dp->hw_thread, 0, - a2dp_playback_hw_thread, data); + err = pthread_create(&data->hw_thread, 0, playback_hw_thread, data); return -err; } -static int bluetooth_a2dp_playback_stop(snd_pcm_ioplug_t *io) +static int bluetooth_playback_stop(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; DBG("%p", io); - a2dp->stopped = 1; + data->stopped = 1; return 0; } @@ -225,28 +221,27 @@ static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) static void bluetooth_exit(struct bluetooth_data *data) { + struct bluetooth_a2dp *a2dp = &data->a2dp; + if (data->sock >= 0) close(data->sock); if (data->stream_fd >= 0) close(data->stream_fd); - if (data->cfg.codec == CFG_CODEC_SBC) { - struct bluetooth_a2dp *a2dp = &data->a2dp; - - if (a2dp->hw_thread) { - pthread_cancel(a2dp->hw_thread); - pthread_join(a2dp->hw_thread, 0); - } + if (data->hw_thread) { + pthread_cancel(data->hw_thread); + pthread_join(data->hw_thread, 0); + } + if (data->cfg.codec == CFG_CODEC_SBC) sbc_finish(&a2dp->sbc); - } - if (data->a2dp.pipefd[0] > 0) - close(data->a2dp.pipefd[0]); + if (data->pipefd[0] > 0) + close(data->pipefd[0]); - if (data->a2dp.pipefd[1] > 0) - close(data->a2dp.pipefd[1]); + if (data->pipefd[1] > 0) + close(data->pipefd[1]); free(data); } @@ -279,8 +274,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - /* a2dp : wake up any client polling at us */ - return write(data->a2dp.pipefd[1], &c, 1); + /* wake up any client polling at us */ + return write(data->pipefd[1], &c, 1); } static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, @@ -366,26 +361,26 @@ static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, return 0; } -static int bluetooth_a2dp_playback_poll_descriptors(snd_pcm_ioplug_t *io, +static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int space) { struct bluetooth_data *data = io->private_data; DBG(""); - assert(data->a2dp.pipefd[0] >= 0); + assert(data->pipefd[0] >= 0); if (space < 1) return 0; - pfd[0].fd = data->a2dp.pipefd[0]; + pfd[0].fd = data->pipefd[0]; pfd[0].events = POLLIN; pfd[0].revents = 0; return 1; } -static int bluetooth_a2dp_playback_poll_revents(snd_pcm_ioplug_t *io, +static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) { @@ -479,6 +474,13 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); + if (io->hw_ptr > io->appl_ptr) { + ret = bluetooth_playback_stop(io); + if (ret == 0) + ret = -EPIPE; + goto done; + } + frame_size = areas->step / 8; if ((data->count + size * frame_size) <= cfg.pkt_len) frames_to_read = size; @@ -505,10 +507,6 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, /* Reset count pointer */ data->count = 0; - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / frame_size) - % io->buffer_size; - ret = frames_to_read; } else if (rsend < 0) ret = (errno == EPIPE) ? -EIO : -errno; @@ -516,7 +514,7 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, ret = -EIO; done: - DBG("returning %lu", ret); + DBG("returning %ld", ret); return ret; } @@ -576,7 +574,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, DBG("hw_ptr=%lu appl_ptr=%lu", io->hw_ptr, io->appl_ptr); if (io->hw_ptr > io->appl_ptr) { - ret = bluetooth_a2dp_playback_stop(io); + ret = bluetooth_playback_stop(io); if (ret == 0) ret = -EPIPE; goto done; @@ -656,15 +654,15 @@ done: } static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { - .start = bluetooth_start, - .stop = bluetooth_stop, + .start = bluetooth_playback_start, + .stop = bluetooth_playback_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, + .poll_descriptors = bluetooth_playback_poll_descriptors, + .poll_revents = bluetooth_playback_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { @@ -680,15 +678,15 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { - .start = bluetooth_a2dp_playback_start, - .stop = bluetooth_a2dp_playback_stop, + .start = bluetooth_playback_start, + .stop = bluetooth_playback_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_write, - .poll_descriptors = bluetooth_a2dp_playback_poll_descriptors, - .poll_revents = bluetooth_a2dp_playback_poll_revents, + .poll_descriptors = bluetooth_playback_poll_descriptors, + .poll_revents = bluetooth_playback_poll_revents, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { @@ -886,15 +884,10 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * a2dp->sbc.channels * 2; a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - a2dp->pipefd[0] = -1; - a2dp->pipefd[1] = -1; - if (pipe(a2dp->pipefd) < 0) - return -errno; - if (fcntl(a2dp->pipefd[0], F_SETFL, O_NONBLOCK) < 0) - return -errno; - if (fcntl(a2dp->pipefd[1], F_SETFL, O_NONBLOCK) < 0) - return -errno; + DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, + a2dp->sbc.bitpool); return 0; } @@ -1016,9 +1009,6 @@ done: ret = bluetooth_a2dp_init(data, sbc); if (ret < 0) return ret; - DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", - a2dp->sbc.allocation, a2dp->sbc.subbands, - a2dp->sbc.blocks, a2dp->sbc.bitpool); } if (data->stream_fd == -1) { @@ -1071,6 +1061,16 @@ static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf) data->sock = sk; + data->pipefd[0] = -1; + data->pipefd[1] = -1; + + if (pipe(data->pipefd) < 0) + return -errno; + if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) + return -errno; + if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) + return -errno; + return bluetooth_cfg(data, conf); } -- cgit From c6063f0ac4917bbfa9ec91f56247d6ed2e4725b9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 27 Aug 2007 22:18:39 +0000 Subject: Ignore error so hsp device can work for unpatched kernels. --- audio/pcm_bluetooth.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index e0264018..734eae7f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -305,7 +305,8 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, SNDERR("%s (%d)", strerror(err), err); - return -err; + /* FIXME: We should not ignores errors on future. */ + return 0; } static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, -- cgit From ce98ca977ac70c82e721505ca5423471d9a13f60 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 27 Aug 2007 22:22:30 +0000 Subject: Fix comment. --- audio/pcm_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 734eae7f..d031b318 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -305,7 +305,7 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, SNDERR("%s (%d)", strerror(err), err); - /* FIXME: We should not ignores errors on future. */ + /* FIXME: We should not ignores errors in the future. */ return 0; } -- cgit From 770f024d7ca279390820569f46061830b3b89330 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 08:43:31 +0000 Subject: Handle alsa+HSP better on client disconnect --- audio/headset.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index da224b8a..1a4b1171 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1586,6 +1586,8 @@ gboolean headset_unlock(struct device *dev, void *data) hs->locked = FALSE; + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + return TRUE; } -- cgit From 4327a133c205d5fbeb3a823f62d2f6fa3cc73279 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 08:47:33 +0000 Subject: Make headset_unlock a no-op if the headset isn't locked --- audio/headset.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1a4b1171..bcaa4912 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1584,6 +1584,9 @@ gboolean headset_unlock(struct device *dev, void *data) { struct headset *hs = dev->headset; + if (!hs->locked) + return FALSE; + hs->locked = FALSE; headset_set_state(dev, HEADSET_STATE_DISCONNECTED); -- cgit From b72f1302fa22e420431356a61a80f5f045f75c97 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 08:55:00 +0000 Subject: headset_unlock: only disconnect if necessary --- audio/headset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index bcaa4912..0ccd9d2e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1589,7 +1589,8 @@ gboolean headset_unlock(struct device *dev, void *data) hs->locked = FALSE; - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + if (hs->state > HEADSET_STATE_DISCONNECTED) + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return TRUE; } -- cgit From e2827b35575bab20a99a170805a0a59a32fb2c48 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 09:54:21 +0000 Subject: Make SBC Audio Source count configurable and the default value 1 --- audio/a2dp.c | 12 ++++++------ audio/a2dp.h | 3 +-- audio/audio.conf | 6 ++---- audio/main.c | 19 ++++++++++++++++--- audio/manager.c | 16 ++++++++++++++-- audio/manager.h | 2 +- 6 files changed, 40 insertions(+), 18 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index d3cedefc..fad9bd25 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -656,21 +656,21 @@ add: return sep; } -int a2dp_init(DBusConnection *conn, gboolean enable_sink, gboolean enable_source) +int a2dp_init(DBusConnection *conn, int sources, int sinks) { - if (!enable_sink && !enable_source) + int i; + + if (!sources && !sinks) return 0; connection = dbus_connection_ref(conn); avdtp_init(); - if (enable_sink) { - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); + for (i = 0; i < sources; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); - } - if (enable_source) + for (i = 0; i < sinks; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); return 0; diff --git a/audio/a2dp.h b/audio/a2dp.h index 0a838b62..4804c5f1 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -64,8 +64,7 @@ typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev, struct avdtp_stream *stream, void *user_data); -int a2dp_init(DBusConnection *conn, gboolean enable_sink, - gboolean enable_source); +int a2dp_init(DBusConnection *conn, int sources, int sinks); void a2dp_exit(void); unsigned int a2dp_source_request_stream(struct avdtp *session, diff --git a/audio/audio.conf b/audio/audio.conf index f88668e4..97fd90f4 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -21,7 +21,5 @@ SCORouting=PCM DisableHFP=true # Just an example of potential config options for the other interfaces -#[Sink] -#Codecs=SBC,MPEG12 -#SBCChannelMode=joint -#SBCBitpool=51 +#[A2DP] +#SourceCount=2 diff --git a/audio/main.c b/audio/main.c index ae43f170..8e27190c 100644 --- a/audio/main.c +++ b/audio/main.c @@ -43,6 +43,7 @@ static gboolean disable_hfp = TRUE; static gboolean sco_hci = FALSE; +static int source_count = 1; static GMainLoop *main_loop = NULL; @@ -123,9 +124,20 @@ static void read_config(const char *file) } else disable_hfp = no_hfp; - debug("Config options: DisableHFP=%s, SCORouting=%s", + str = g_key_file_get_string(keyfile, "A2DP", + "SourceCount", &err); + if (err) { + debug("%s: %s", file, err->message); + g_error_free(err); + err = NULL; + } else { + source_count = atoi(str); + g_free(str); + } + + debug("Config options: DisableHFP=%s, SCORouting=%s, SourceCount=%d", disable_hfp ? "true" : "false", - sco_hci ? "HCI" : "PCM"); + sco_hci ? "HCI" : "PCM", source_count); g_key_file_free(keyfile); } @@ -164,7 +176,8 @@ int main(int argc, char *argv[]) exit(1); } - if (audio_init(conn, &enabled, disable_hfp, sco_hci) < 0) { + if (audio_init(conn, &enabled, disable_hfp, sco_hci, + source_count) < 0) { error("Audio init failed!"); exit(1); } diff --git a/audio/manager.c b/audio/manager.c index 1228b2cc..54b95538 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1567,8 +1567,10 @@ static void server_exit(void) } int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, - gboolean no_hfp, gboolean sco_hci) + gboolean no_hfp, gboolean sco_hci, int source_count) { + int sinks, sources; + connection = dbus_connection_ref(conn); enabled = enable; @@ -1582,7 +1584,17 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, if (headset_server_init(conn, no_hfp) < 0) goto failed; - if (a2dp_init(conn, enable->sink, enable->source) < 0) + if (enable->sink) + sources = source_count; + else + sources = 0; + + if (enable->source) + sinks = 1; + else + sinks = 0; + + if (a2dp_init(conn, sources, sinks) < 0) goto failed; if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, diff --git a/audio/manager.h b/audio/manager.h index 42632d17..ceabe680 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -37,7 +37,7 @@ struct enabled_interfaces { typedef void (*create_dev_cb_t) (struct device *dev, void *user_data); int audio_init(DBusConnection *conn, struct enabled_interfaces *enabled, - gboolean no_hfp, gboolean sco_hci); + gboolean no_hfp, gboolean sco_hci, int source_count); void audio_exit(void); -- cgit From 1e7c36a80b7110b50bc71762f633af1a46b4609c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 11:49:37 +0000 Subject: Make HCI routing the default --- audio/audio.conf | 2 +- audio/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/audio.conf b/audio/audio.conf index 97fd90f4..3fe8965f 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -10,7 +10,7 @@ # SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA) # Defaults to HCI -SCORouting=PCM +#SCORouting=PCM # Headset interface specific options (i.e. options which affect how the audio # service interacts with remote headset devices) diff --git a/audio/main.c b/audio/main.c index 8e27190c..e828f022 100644 --- a/audio/main.c +++ b/audio/main.c @@ -42,7 +42,7 @@ #include "manager.h" static gboolean disable_hfp = TRUE; -static gboolean sco_hci = FALSE; +static gboolean sco_hci = TRUE; static int source_count = 1; static GMainLoop *main_loop = NULL; -- cgit From 10abd63aeb3665b2dcade32b6397a485579638ec Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 12:01:15 +0000 Subject: Minor coding style cleanup --- audio/main.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index e828f022..fb199a4b 100644 --- a/audio/main.c +++ b/audio/main.c @@ -77,8 +77,7 @@ static void read_config(const char *file) return; } - str = g_key_file_get_string(keyfile, "General", - "SCORouting", &err); + str = g_key_file_get_string(keyfile, "General", "SCORouting", &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); @@ -93,8 +92,7 @@ static void read_config(const char *file) g_free(str); } - str = g_key_file_get_string(keyfile, "General", - "Disable", &err); + str = g_key_file_get_string(keyfile, "General", "Disable", &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); @@ -115,8 +113,8 @@ static void read_config(const char *file) g_free(str); } - no_hfp = g_key_file_get_boolean(keyfile, "Headset", - "DisableHFP", &err); + no_hfp = g_key_file_get_boolean(keyfile, "Headset", "DisableHFP", + &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); @@ -124,8 +122,7 @@ static void read_config(const char *file) } else disable_hfp = no_hfp; - str = g_key_file_get_string(keyfile, "A2DP", - "SourceCount", &err); + str = g_key_file_get_string(keyfile, "A2DP", "SourceCount", &err); if (err) { debug("%s: %s", file, err->message); g_error_free(err); -- cgit From d1fb0b25a9882016de70f102408f10101b348e57 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 12:22:29 +0000 Subject: Fix unix client disconnects before headset has been successfully connected --- audio/a2dp.c | 2 +- audio/a2dp.h | 2 +- audio/headset.c | 42 +++++++++++++++++++++++++++++++++++++++--- audio/headset.h | 1 + audio/unix.c | 9 +++++++-- 5 files changed, 49 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index fad9bd25..5e08b25b 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -880,7 +880,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, sink_new_stream(setup->dev, session, setup->stream); } -gboolean a2dp_source_cancel_stream(int id) +gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) { struct a2dp_stream_cb *cb_data; GSList *l; diff --git a/audio/a2dp.h b/audio/a2dp.h index 4804c5f1..7358473e 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -72,7 +72,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, gboolean start, a2dp_stream_cb_t cb, void *user_data, struct a2dp_sep **sep); -gboolean a2dp_source_cancel_stream(int id); +gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id); gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session); diff --git a/audio/headset.c b/audio/headset.c index 0ccd9d2e..5a626c17 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -68,6 +68,7 @@ static char *str_state[] = {"DISCONNECTED", "CONNECTING", "CONNECTED", struct pending_connect { DBusMessage *msg; + DBusPendingCall *call; GIOChannel *io; guint io_id; int sock; @@ -114,6 +115,11 @@ static void pending_connect_free(struct pending_connect *c) } if (c->msg) dbus_message_unref(c->msg); + if (c->call) { + dbus_pending_call_cancel(c->call); + dbus_pending_call_unref(c->call); + } + g_free(c); } @@ -780,7 +786,7 @@ failed: headset_set_state(device, HEADSET_STATE_DISCONNECTED); } -static int get_handles(struct device *device) +static int get_handles(struct device *device, struct pending_connect *c) { DBusPendingCall *pending; struct headset *hs = device->headset; @@ -816,7 +822,10 @@ static int get_handles(struct device *device) } dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); - dbus_pending_call_unref(pending); + if (c) + c->call = pending; + else + dbus_pending_call_unref(pending); dbus_message_unref(msg); return 0; @@ -836,7 +845,7 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; if (hs->state == HEADSET_STATE_DISCONNECTED) - return get_handles(device); + return get_handles(device, c); else return 0; } @@ -1399,6 +1408,33 @@ void headset_free(struct device *dev) dev->headset = NULL; } +gboolean headset_cancel_stream(struct device *dev, unsigned int id) +{ + struct headset *hs = dev->headset; + GSList *l; + struct pending_connect *pending = NULL; + + for (l = hs->pending; l != NULL; l = l->next) { + struct pending_connect *tmp = l->data; + + if (tmp->id == id) { + pending = tmp; + break; + } + } + + if (!pending) + return FALSE; + + hs->pending = g_slist_remove(hs->pending, pending); + pending_connect_free(pending); + + if (!hs->pending) + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + + return TRUE; +} + unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, void *user_data) { diff --git a/audio/headset.h b/audio/headset.h index 31486834..3f762815 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -57,6 +57,7 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, void *user_data); +gboolean headset_cancel_stream(struct device *dev, unsigned int id); headset_type_t headset_get_type(struct device *dev); void headset_set_type(struct device *dev, headset_type_t type); diff --git a/audio/unix.c b/audio/unix.c index 1d1a0b6a..71bb480c 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -79,6 +79,7 @@ struct unix_client { notify_cb_t disconnect; notify_cb_t suspend; notify_cb_t play; + gboolean (*cancel_stream) (struct device *dev, unsigned int id); }; static GSList *clients = NULL; @@ -248,6 +249,8 @@ static void headset_setup_complete(struct device *dev, void *user_data) return; } + headset_lock(dev, NULL); + memset(&cfg, 0, sizeof(cfg)); cfg.fd_opt = CFG_FD_OPT_READWRITE; @@ -401,10 +404,12 @@ static void create_stream(struct device *dev, struct unix_client *client) id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client, &a2dp->sep); + client->cancel_stream = a2dp_source_cancel_stream; break; case TYPE_HEADSET: id = headset_request_stream(dev, headset_setup_complete, client); + client->cancel_stream = headset_cancel_stream; break; default: error("No known services for device"); @@ -525,8 +530,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) goto failed; if (client->disconnect) client->disconnect(client->dev, cb_data); - if (client->type == TYPE_SINK && client->req_id > 0) - a2dp_source_cancel_stream(client->req_id); + if (client->cancel_stream && client->req_id > 0) + client->cancel_stream(client->dev, client->req_id); goto failed; } -- cgit From 25c5aaa285d2e9767ee3be8fb4cd97411307da20 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 28 Aug 2007 12:23:34 +0000 Subject: Reset request id for headset connects too --- audio/unix.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 71bb480c..6c420a24 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -243,6 +243,8 @@ static void headset_setup_complete(struct device *dev, void *user_data) struct ipc_data_cfg cfg; int fd; + client->req_id = 0; + if (!dev) { unix_send_cfg(client->sock, NULL, -1); client->dev = NULL; -- cgit From c49a76d6e5b000921509df4e071d8110bbe7e3d8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 28 Aug 2007 16:03:33 +0000 Subject: Disable the GStreamer A2DP sink code for now --- audio/gstbluetooth.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 8b2ca76e..7e02e202 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -67,9 +67,11 @@ static gboolean plugin_init(GstPlugin *plugin) GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE) return FALSE; +#if 0 if (gst_element_register(plugin, "a2dpsink", GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) return FALSE; +#endif return TRUE; } -- cgit From 9f717b22e55e7a63de3bac3c9f11cc6075cef5f1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 28 Aug 2007 16:13:44 +0000 Subject: We are not going to support PulseAudio any time soon --- audio/Makefile.am | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 7d23d83a..b5e3c466 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -34,17 +34,6 @@ libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ endif -if PULSE -pulsedir = $(libdir)/pulse-0.9/modules - -noinst_LTLIBRARIES = module-bluetooth-sink.la - -module_bluetooth_sink_la_SOURCES = module-bluetooth-sink.c ipc.h -module_bluetooth_sink_la_LDFLAGS = -module -avoid-version -module_bluetooth_sink_la_LIBADD = @SBC_LIBS@ @PULSE_LIBS@ -module_bluetooth_sink_la_CFLAGS = @PULSE_CFLAGS@ @SBC_CFLAGS@ -endif - if GSTREAMER gstreamerdir = $(libdir)/gstreamer-0.10 -- cgit From 986c273d9f5bc0970fb06ec755ba3ebca0220c60 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 29 Aug 2007 09:21:34 +0000 Subject: Only export the needed symbols for the plugins --- audio/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index b5e3c466..a670fced 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -24,12 +24,12 @@ alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h -libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h -libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_ctl_.* libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ endif @@ -44,7 +44,7 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ gsta2dpsink.h gsta2dpsink.c -libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-dynamic +libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*\(gst_\|Gst\|GST_\).* libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif -- cgit From dad4c62b54ab078c1d5525e80be9c6391ea1731a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 29 Aug 2007 11:23:12 +0000 Subject: Add GetName method to Device interface --- audio/device.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 2c06233e..4f47bcb2 100644 --- a/audio/device.c +++ b/audio/device.c @@ -45,6 +45,7 @@ #include "logging.h" #include "textfile.h" +#include "error.h" #include "ipc.h" #include "device.h" #include "avdtp.h" @@ -70,6 +71,54 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, return send_message_and_unref(conn, reply); } +static DBusHandlerResult device_get_name(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct device *device = data; + DBusMessage *reply, *reply2, *msg2; + DBusError derr; + const char *name; + char address[18], *addr_ptr = address; + + msg2 = dbus_message_new_method_call("org.bluez", device->adapter_path, + "org.bluez.Adapter", "GetRemoteName"); + if (!msg2) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + ba2str(&device->dst, address); + dbus_message_append_args(msg2, DBUS_TYPE_STRING, &addr_ptr, + DBUS_TYPE_INVALID); + + dbus_error_init(&derr); + reply2 = dbus_connection_send_with_reply_and_block(conn, msg2, -1, + &derr); + + dbus_message_unref(msg2); + + if (dbus_error_is_set(&derr)) { + error("%s GetRemoteName(): %s", device->adapter_path, + derr.message); + dbus_error_free(&derr); + return err_failed(conn, msg, "Unable to get remote name"); + } + + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_get_args(reply2, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + dbus_message_unref(reply2); + + return send_message_and_unref(conn, reply); +} + static DBusHandlerResult device_get_connected(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -102,7 +151,8 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, static DBusMethodVTable device_methods[] = { { "GetAddress", device_get_address, "", "s" }, - { "GetConnectedInterfaces", device_get_connected, "", "s" }, + { "GetName", device_get_name, "", "s" }, + { "GetConnectedInterfaces", device_get_connected, "", "as" }, { NULL, NULL, NULL, NULL } }; -- cgit From 60fa5f6cfbfe82c239bb957c1a3298bceaa62604 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 29 Aug 2007 11:30:47 +0000 Subject: Only export gst_plugin_desc --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index a670fced..c54a8e72 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -44,7 +44,7 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ gsta2dpsink.h gsta2dpsink.c -libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*\(gst_\|Gst\|GST_\).* +libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif -- cgit From c13f5169711913b9b6d3ee40740480fe234491cd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 29 Aug 2007 11:41:15 +0000 Subject: Add GetAdapter method and update API documentation --- audio/audio-api.txt | 11 ++++++++++- audio/device.c | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index c49b61b9..4fbed84a 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -105,7 +105,16 @@ Methods string GetAddress() [experimental] Returns the Bluetooth address of the remote device. - array{string} GetConnectedInterfaces() [experimental] + string GetAdapter() [experimental] + + Returns the address of the local adapter that the + device is associated with. + + string GetName() [experimental] + + Returns a friendly name for the device. + + array{string} GetConnectedInterfaces() [experimental] Returns a string list of interfaces that are in a connected state. diff --git a/audio/device.c b/audio/device.c index 4f47bcb2..9c90ed43 100644 --- a/audio/device.c +++ b/audio/device.c @@ -119,6 +119,26 @@ static DBusHandlerResult device_get_name(DBusConnection *conn, return send_message_and_unref(conn, reply); } +static DBusHandlerResult device_get_adapter(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct device *device = data; + DBusMessage *reply; + char address[18], *ptr = address; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + ba2str(&device->src, address); + + dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, + DBUS_TYPE_INVALID); + + return send_message_and_unref(conn, reply); +} + + static DBusHandlerResult device_get_connected(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -152,6 +172,7 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, static DBusMethodVTable device_methods[] = { { "GetAddress", device_get_address, "", "s" }, { "GetName", device_get_name, "", "s" }, + { "GetAdapter", device_get_adapter, "", "s" }, { "GetConnectedInterfaces", device_get_connected, "", "as" }, { NULL, NULL, NULL, NULL } }; -- cgit From 3eb397c28e2ab0999a069dc9c0404d9cfaca9ce2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 29 Aug 2007 12:19:12 +0000 Subject: Check for NULL before calling device_store --- audio/manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 54b95538..9c57d17d 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -838,7 +838,8 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, DBUS_TYPE_STRING, ¶m, DBUS_TYPE_INVALID); - device_store(default_dev, TRUE); + if (default_dev) + device_store(default_dev, TRUE); } dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, -- cgit From 170d40808c0307040cf01a686e57684888e13b26 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 29 Aug 2007 12:29:42 +0000 Subject: Remove stream callback in sink_free --- audio/sink.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 4bebf738..5b699fb9 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -52,6 +52,7 @@ struct pending_request { struct sink { struct avdtp *session; struct avdtp_stream *stream; + unsigned int cb_id; uint8_t state; struct pending_request *connect; struct pending_request *disconnect; @@ -104,6 +105,7 @@ static void stream_state_changed(struct avdtp_stream *stream, sink->session = NULL; } sink->stream = NULL; + sink->cb_id = 0; break; case AVDTP_STATE_OPEN: if (old_state == AVDTP_STATE_CONFIGURED) @@ -291,6 +293,10 @@ void sink_free(struct device *dev) { struct sink *sink = dev->sink; + if (sink->cb_id) + avdtp_stream_remove_cb(sink->session, sink->stream, + sink->cb_id); + if (sink->session) avdtp_unref(sink->session); @@ -335,7 +341,8 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->stream = stream; sink->initiator = FALSE; - avdtp_stream_add_cb(session, stream, stream_state_changed, dev); + sink->cb_id = avdtp_stream_add_cb(session, stream, + stream_state_changed, dev); return TRUE; } -- cgit From 5402a5058f463efd3bc84d43b1af10ff253b9a1e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 29 Aug 2007 19:38:37 +0000 Subject: Make codec parameters support available for application. --- audio/a2dp.c | 432 +++++++++++++++++++++++++++++--------------------- audio/a2dp.h | 4 +- audio/avdtp.c | 67 ++++++-- audio/avdtp.h | 6 +- audio/pcm_bluetooth.c | 116 ++++++++++++-- audio/sink.c | 3 +- audio/unix.c | 129 ++++++++++++++- 7 files changed, 545 insertions(+), 212 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 5e08b25b..5ecd8159 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -76,6 +76,7 @@ struct a2dp_stream_setup { struct avdtp *session; struct device *dev; struct avdtp_stream *stream; + struct avdtp_service_capability *media_codec; gboolean start; gboolean canceled; GSList *cb; @@ -138,6 +139,188 @@ static void stream_state_changed(struct avdtp_stream *stream, sep->stream = NULL; } +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) +{ + switch (freq) { + case A2DP_SAMPLING_FREQ_16000: + case A2DP_SAMPLING_FREQ_32000: + return 53; + case A2DP_SAMPLING_FREQ_44100: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 31; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 53; + default: + error("Invalid channel mode %u", mode); + return 53; + } + case A2DP_SAMPLING_FREQ_48000: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 29; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 51; + default: + error("Invalid channel mode %u", mode); + return 51; + } + default: + error("Invalid sampling freq %u", freq); + return 53; + } +} + +static gboolean select_sbc_params(struct sbc_codec_cap *cap, + struct sbc_codec_cap *supported) +{ + uint max_bitpool, min_bitpool; + + memset(cap, 0, sizeof(struct sbc_codec_cap)); + + cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + cap->cap.media_codec_type = A2DP_CODEC_SBC; + + if (supported->frequency & A2DP_SAMPLING_FREQ_48000) + cap->frequency = A2DP_SAMPLING_FREQ_48000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) + cap->frequency = A2DP_SAMPLING_FREQ_44100; + else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) + cap->frequency = A2DP_SAMPLING_FREQ_32000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) + cap->frequency = A2DP_SAMPLING_FREQ_16000; + else { + error("No supported frequencies"); + return FALSE; + } + + if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) + cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + else { + error("No supported channel modes"); + return FALSE; + } + + if (supported->block_length & A2DP_BLOCK_LENGTH_16) + cap->block_length = A2DP_BLOCK_LENGTH_16; + else if (supported->block_length & A2DP_BLOCK_LENGTH_12) + cap->block_length = A2DP_BLOCK_LENGTH_12; + else if (supported->block_length & A2DP_BLOCK_LENGTH_8) + cap->block_length = A2DP_BLOCK_LENGTH_8; + else if (supported->block_length & A2DP_BLOCK_LENGTH_4) + cap->block_length = A2DP_BLOCK_LENGTH_4; + else { + error("No supported block lengths"); + return FALSE; + } + + if (supported->subbands & A2DP_SUBBANDS_8) + cap->subbands = A2DP_SUBBANDS_8; + else if (supported->subbands & A2DP_SUBBANDS_4) + cap->subbands = A2DP_SUBBANDS_4; + else { + error("No supported subbands"); + return FALSE; + } + + if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) + cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + else if (supported->allocation_method & A2DP_ALLOCATION_SNR) + cap->allocation_method = A2DP_ALLOCATION_SNR; + + min_bitpool = MAX(2, supported->min_bitpool); + max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + supported->max_bitpool); + + cap->min_bitpool = min_bitpool; + cap->max_bitpool = max_bitpool; + + return TRUE; +} + +static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, + GSList **caps) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap; + + if (!setup) + return FALSE; + + if (setup->media_codec) + memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); + else { + media_codec = avdtp_get_codec(rsep); + if (!media_codec) + return FALSE; + + select_sbc_params(&sbc_cap, + (struct sbc_codec_cap *) media_codec->data); + } + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + *caps = g_slist_append(*caps, media_codec); + + return TRUE; +} + +static void discovery_complete(struct avdtp *session, GSList *seps, int err, + void *user_data) +{ + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + GSList *caps = NULL; + + if (err < 0) { + setup->stream = NULL; + finalize_stream_setup(setup); + return; + } + + debug("Discovery complete"); + + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + A2DP_CODEC_SBC, &lsep, &rsep) < 0) { + error("No matching ACP and INT SEPs found"); + finalize_stream_setup(setup); + return; + } + + if (!a2dp_select_capabilities(rsep, &caps)) { + error("Unable to select remote SEP capabilities"); + finalize_stream_setup(setup); + return; + } + + err = avdtp_set_configuration(session, rsep, lsep, caps, + &setup->stream); + if (err < 0) { + error("avdtp_set_configuration: %s", strerror(-err)); + finalize_stream_setup(setup); + return; + } + + /* Notify sink.c of the new stream */ + sink_new_stream(setup->dev, session, setup->stream); +} + static gboolean setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, @@ -328,7 +511,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, error("avdtp_start failed"); setup->stream = NULL; - } + } finalize: finalize_stream_setup(setup); @@ -456,6 +639,30 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Sink: Close_Cfm"); else debug("SBC Source: Close_Cfm"); + + if (!setup) + return; + + if (setup->canceled) { + stream_setup_free(setup); + return; + } + + if (err) { + setup->stream = NULL; + goto finalize; + } + + if (setup->start) { + if (avdtp_discover(session, discovery_complete, setup) == 0) + return; + + error("avdtp_discover failed"); + setup->stream = NULL; + } + +finalize: + finalize_stream_setup(setup); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -506,6 +713,32 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); + + if (!setup) + return; + + if (setup->canceled) { + if (!err) + avdtp_close(session, stream); + stream_setup_free(setup); + return; + } + + if (err) { + setup->stream = NULL; + goto finalize; + } + + if (setup->start) { + if (avdtp_start(session, stream) == 0) + return; + + error("avdtp_start failed"); + setup->stream = NULL; + } + +finalize: + finalize_stream_setup(setup); } static struct avdtp_sep_cfm cfm = { @@ -705,181 +938,6 @@ void a2dp_exit() dbus_connection_unref(connection); } -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { - switch (freq) { - case A2DP_SAMPLING_FREQ_16000: - case A2DP_SAMPLING_FREQ_32000: - return 53; - case A2DP_SAMPLING_FREQ_44100: - switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 31; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: - return 53; - default: - error("Invalid channel mode %u", mode); - return 53; - } - case A2DP_SAMPLING_FREQ_48000: - switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 29; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: - return 51; - default: - error("Invalid channel mode %u", mode); - return 51; - } - default: - error("Invalid sampling freq %u", freq); - return 53; - } -} - -static gboolean select_sbc_params(struct sbc_codec_cap *cap, - struct sbc_codec_cap *supported) -{ - uint max_bitpool, min_bitpool; - - memset(cap, 0, sizeof(struct sbc_codec_cap)); - - cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - cap->cap.media_codec_type = A2DP_CODEC_SBC; - - if (supported->frequency & A2DP_SAMPLING_FREQ_48000) - cap->frequency = A2DP_SAMPLING_FREQ_48000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) - cap->frequency = A2DP_SAMPLING_FREQ_44100; - else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) - cap->frequency = A2DP_SAMPLING_FREQ_32000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) - cap->frequency = A2DP_SAMPLING_FREQ_16000; - else { - error("No supported frequencies"); - return FALSE; - } - - if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) - cap->channel_mode = A2DP_CHANNEL_MODE_MONO; - else { - error("No supported channel modes"); - return FALSE; - } - - if (supported->block_length & A2DP_BLOCK_LENGTH_16) - cap->block_length = A2DP_BLOCK_LENGTH_16; - else if (supported->block_length & A2DP_BLOCK_LENGTH_12) - cap->block_length = A2DP_BLOCK_LENGTH_12; - else if (supported->block_length & A2DP_BLOCK_LENGTH_8) - cap->block_length = A2DP_BLOCK_LENGTH_8; - else if (supported->block_length & A2DP_BLOCK_LENGTH_4) - cap->block_length = A2DP_BLOCK_LENGTH_4; - else { - error("No supported block lengths"); - return FALSE; - } - - if (supported->subbands & A2DP_SUBBANDS_8) - cap->subbands = A2DP_SUBBANDS_8; - else if (supported->subbands & A2DP_SUBBANDS_4) - cap->subbands = A2DP_SUBBANDS_4; - else { - error("No supported subbands"); - return FALSE; - } - - if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - else if (supported->allocation_method & A2DP_ALLOCATION_SNR) - cap->allocation_method = A2DP_ALLOCATION_SNR; - - min_bitpool = MAX(2, supported->min_bitpool); - max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), - supported->max_bitpool); - - cap->min_bitpool = min_bitpool; - cap->max_bitpool = max_bitpool; - - return TRUE; -} - -static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) -{ - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap, *acp_sbc; - - media_codec = avdtp_get_codec(rsep); - if (!media_codec) - return FALSE; - - acp_sbc = (void *) media_codec->data; - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - select_sbc_params(&sbc_cap, acp_sbc); - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); - - *caps = g_slist_append(*caps, media_codec); - - return TRUE; -} - -static void discovery_complete(struct avdtp *session, GSList *seps, int err, - void *user_data) -{ - struct avdtp_local_sep *lsep; - struct avdtp_remote_sep *rsep; - GSList *caps = NULL; - - if (err < 0) { - error("Discovery failed: %s (%d)", strerror(-err), -err); - setup->stream = NULL; - finalize_stream_setup(setup); - return; - } - - debug("Discovery complete"); - - if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, - A2DP_CODEC_SBC, &lsep, &rsep) < 0) { - error("No matching ACP and INT SEPs found"); - finalize_stream_setup(setup); - return; - } - - if (!a2dp_select_capabilities(rsep, &caps)) { - error("Unable to select remote SEP capabilities"); - finalize_stream_setup(setup); - return; - } - - err = avdtp_set_configuration(session, rsep, lsep, caps, - &setup->stream); - if (err < 0) { - error("avdtp_set_configuration: %s", strerror(-err)); - finalize_stream_setup(setup); - return; - } - - /* Notify sink.c of the new stream */ - sink_new_stream(setup->dev, session, setup->stream); -} - gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) { struct a2dp_stream_cb *cb_data; @@ -914,7 +972,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, gboolean start, a2dp_stream_cb_t cb, void *user_data, - struct a2dp_sep **ret) + struct a2dp_sep **ret, + struct avdtp_service_capability *media_codec) { struct a2dp_stream_cb *cb_data; static unsigned int cb_id = 0; @@ -967,6 +1026,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup->cb = g_slist_append(setup->cb, cb_data); setup->start = start; setup->stream = sep->stream; + setup->media_codec = media_codec; switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: @@ -982,7 +1042,21 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, } if (sep->starting) break; - if (avdtp_start(session, sep->stream) < 0) { + if (setup->media_codec) { + if (avdtp_stream_has_capability(setup->stream, + setup->media_codec)) { + if (avdtp_start(session, sep->stream) < 0) { + error("avdtp_start failed"); + goto failed; + } + } else { + if (avdtp_close(session, sep->stream) < 0) { + error("avdtp_close failed"); + goto failed; + } + } + } + else if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } @@ -1006,7 +1080,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, error("SEP in bad state for requesting a new stream"); goto failed; } - + if (ret) *ret = sep; diff --git a/audio/a2dp.h b/audio/a2dp.h index 7358473e..ede0c70b 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -71,11 +71,11 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data, - struct a2dp_sep **sep); + struct a2dp_sep **sep, + struct avdtp_service_capability *media_codec); gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id); gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session); gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session); gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session); - diff --git a/audio/avdtp.c b/audio/avdtp.c index a967be76..d03afbae 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -552,9 +552,6 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); - if (stream->io) - g_source_remove(stream->io); - g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); g_slist_free(stream->callbacks); @@ -718,7 +715,7 @@ void avdtp_unref(struct avdtp *session) session->sock = -1; } - if (session->sock >= 0) + if (session->sock >= 0) set_disconnect_timer(session); else if (!session->free_lock) /* Drop the local ref if we aren't connected */ @@ -954,7 +951,7 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, err = AVDTP_SEP_IN_USE; goto failed; } - + stream = g_new0(struct avdtp_stream, 1); stream->session = session; stream->lsep = sep; @@ -1074,7 +1071,7 @@ static gboolean avdtp_start_cmd(struct avdtp *session, struct start_req *req, error("Too short start request"); return FALSE; } - + seid_count = 1 + size - sizeof(struct start_req); seid = &req->first_seid; @@ -1936,8 +1933,8 @@ static gboolean avdtp_abort_resp(struct avdtp *session, { struct avdtp_local_sep *sep = stream->lsep; - if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); + if (sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); @@ -2196,7 +2193,7 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst) { struct avdtp *session; - + session = find_session(src, dst); if (!session) @@ -2208,6 +2205,24 @@ gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst) return FALSE; } +gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, + struct avdtp_service_capability *cap) +{ + GSList *l; + struct avdtp_service_capability *stream_cap; + + for (l = stream->caps; l; l = g_slist_next(l)) { + stream_cap = l->data; + if (stream_cap->category == cap->category && + stream_cap->length == cap->length) { + if (!memcmp(stream_cap->data, cap->data, cap->length)) + return TRUE; + } + } + + return FALSE; +} + gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *mtu, GSList **caps) { @@ -2219,7 +2234,7 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, if (mtu) *mtu = stream->mtu; - + if (caps) *caps = stream->caps; @@ -2452,9 +2467,14 @@ int avdtp_set_configuration(struct avdtp *session, return ret; } -int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) +int avdtp_reconfigure(struct avdtp *session, GSList *caps, + struct avdtp_stream *stream) { - struct seid_req req; + struct reconf_req *req; + unsigned char *ptr; + int caps_len; + GSList *l; + struct avdtp_service_capability *cap; if (!g_slist_find(session->streams, stream)) return -EINVAL; @@ -2462,11 +2482,26 @@ int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream) if (stream->lsep->state != AVDTP_STATE_OPEN) return -EINVAL; - memset(&req, 0, sizeof(req)); - init_request(&req.header, AVDTP_GET_CONFIGURATION); - req.acp_seid = stream->rseid; + /* Calculate total size of request */ + for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { + cap = l->data; + caps_len += cap->length + 2; + } + + req = g_malloc0(sizeof(struct reconf_req) + caps_len); + + init_request(&req->header, AVDTP_RECONFIGURE); + req->acp_seid = stream->rseid; + + /* Copy the capabilities into the request */ + for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { + cap = l->data; + memcpy(ptr, cap, cap->length + 2); + ptr += cap->length + 2; + } - return send_request(session, FALSE, NULL, &req, sizeof(req)); + return send_request(session, FALSE, stream, req, sizeof(*req) + + caps_len); } int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) diff --git a/audio/avdtp.h b/audio/avdtp.h index d759be48..d429b435 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -189,6 +189,9 @@ gboolean avdtp_stream_remove_cb(struct avdtp *session, gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *mtu, GSList **caps); +gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, + struct avdtp_service_capability *cap); + int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, @@ -199,7 +202,8 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream); int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_reconfigure(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_reconfigure(struct avdtp *session, GSList *caps, + struct avdtp_stream *stream); int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); int avdtp_close(struct avdtp *session, struct avdtp_stream *stream); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index d031b318..c6dc0931 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -893,19 +893,13 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, return 0; } -static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) { - int ret, total; - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; snd_config_iterator_t i, next; - const char *addr, *pref; - - DBG("Sending PKT_TYPE_CFG_REQ..."); - - memset(buf, 0, sizeof(buf)); + const char *addr, *pref, *mode, *allocation, *rate, *channels, + *subbands, *blocks, *bitpool; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -944,14 +938,114 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) continue; } + if (strcmp(id, "rate") == 0) { + if (snd_config_get_string(n, &rate) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + cfg->rate = strtod(rate, NULL); + continue; + } + + if (strcmp(id, "channels") == 0) { + if (snd_config_get_string(n, &channels) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + cfg->channels = strtod(channels, NULL); + continue; + } + + if (strcmp(id, "channel_mode") == 0) { + if (snd_config_get_string(n, &mode) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + if (strcmp(pref, "mono") == 0) + cfg->channel_mode = CFG_CHANNEL_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->channel_mode = CFG_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->channel_mode = CFG_CHANNEL_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->channel_mode = CFG_CHANNEL_MODE_JOINT_STEREO; + continue; + } + + if (strcmp(id, "allocation") == 0) { + if (snd_config_get_string(n, &allocation) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + if (strcmp(pref, "snr") == 0) + sbc->allocation = CODEC_SBC_ALLOCATION_SNR; + else if (strcmp(pref, "loudness") == 0) + sbc->allocation = CODEC_SBC_ALLOCATION_LOUDNESS; + continue; + } + + if (strcmp(id, "subbands") == 0) { + if (snd_config_get_string(n, &subbands) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + sbc->subbands = strtod(subbands, NULL); + continue; + } + + if (strcmp(id, "blocks") == 0) { + if (snd_config_get_string(n, &blocks) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + sbc->blocks = strtod(blocks, NULL); + continue; + } + + if (strcmp(id, "bitpool") == 0) { + if (snd_config_get_string(n, &bitpool) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + sbc->bitpool = strtod(bitpool, NULL); + continue; + } + SNDERR("Unknown field %s", id); return -EINVAL; } + pkt->length = sizeof(*cfg) + sizeof(*sbc); pkt->type = PKT_TYPE_CFG_REQ; pkt->error = PKT_ERROR_NONE; - ret = send(data->sock, pkt, sizeof(struct ipc_packet), 0); + return 0; +} + +static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +{ + int ret, total; + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + + DBG("Sending PKT_TYPE_CFG_REQ..."); + + memset(buf, 0, sizeof(buf)); + + ret = bluetooth_cfg_init(pkt, conf); + if (ret < 0) + return -ret; + + ret = send(data->sock, pkt, sizeof(*pkt) + pkt->length, 0); if (ret < 0) return -errno; else if (ret == 0) @@ -1026,7 +1120,7 @@ done: while (recv(data->stream_fd, data->buffer, data->cfg.pkt_len, MSG_DONTWAIT) > 0); - memset(data->buffer, 0, data->cfg.pkt_len); + memset(data->buffer, 0, sizeof(data->buffer)); return 0; } diff --git a/audio/sink.c b/audio/sink.c index 5b699fb9..599fdaaf 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -188,7 +188,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->connect = pending; id = a2dp_source_request_stream(sink->session, dev, FALSE, - stream_setup_complete, pending, NULL); + stream_setup_complete, pending, NULL, + NULL); if (id == 0) { pending_request_free(pending); sink->connect = NULL; diff --git a/audio/unix.c b/audio/unix.c index 6c420a24..ef8d6673 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -67,6 +67,7 @@ struct a2dp_data { struct unix_client { struct device *dev; struct avdtp_local_sep *sep; + struct avdtp_service_capability *media_codec; service_type_t type; char *interface; union { @@ -106,6 +107,8 @@ static void client_free(struct unix_client *client) if (client->sock >= 0) close(client->sock); + if (client->media_codec) + g_free(client->media_codec); g_free(client->interface); g_free(client); } @@ -236,7 +239,6 @@ static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) return 0; } - static void headset_setup_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; @@ -405,7 +407,8 @@ static void create_stream(struct device *dev, struct unix_client *client) id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, - client, &a2dp->sep); + client, &a2dp->sep, + client->media_codec); client->cancel_stream = a2dp_source_cancel_stream; break; case TYPE_HEADSET: @@ -442,11 +445,126 @@ static void create_cb(struct device *dev, void *user_data) create_stream(dev, client); } +static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) +{ + struct ipc_codec_sbc *sbc = (void *) cfg->data; + + sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; + + if (cfg->rate > 0) { + switch (cfg->rate) { + case 48000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; + break; + case 44100: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; + break; + case 32000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; + break; + case 16000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; + break; + default: + return -EINVAL; + } + } else { + sbc_cap->frequency = ( A2DP_SAMPLING_FREQ_48000 | + A2DP_SAMPLING_FREQ_44100 | + A2DP_SAMPLING_FREQ_32000 | + A2DP_SAMPLING_FREQ_16000 ); + } + + if (cfg->channel_mode > 0) { + switch (cfg->channel_mode) { + case A2DP_CHANNEL_MODE_JOINT_STEREO: + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + case A2DP_CHANNEL_MODE_MONO: + sbc_cap->channel_mode = cfg->channel_mode; + break; + default: + return -EINVAL; + } + } else { + sbc_cap->channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | + A2DP_CHANNEL_MODE_STEREO | + A2DP_CHANNEL_MODE_DUAL_CHANNEL | + A2DP_CHANNEL_MODE_MONO ); + } + + if (sbc->allocation > 0) { + switch (sbc->allocation) { + case A2DP_ALLOCATION_LOUDNESS: + case A2DP_ALLOCATION_SNR: + sbc_cap->allocation_method = sbc->allocation; + break; + default: + return -EINVAL; + } + } else + sbc_cap->allocation_method = ( A2DP_ALLOCATION_LOUDNESS | + A2DP_ALLOCATION_SNR ); + + if (sbc->subbands > 0) { + switch (sbc->subbands) { + case 8: + sbc_cap->subbands = A2DP_SUBBANDS_8; + break; + case 4: + sbc_cap->subbands = A2DP_SUBBANDS_4; + break; + default: + return -EINVAL; + } + } else + sbc_cap->subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + + if (sbc->blocks > 0) { + switch (sbc->blocks) { + case 16: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; + break; + case 12: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; + break; + case 8: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; + break; + case 4: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; + break; + default: + return -EINVAL; + } + } else { + sbc_cap->block_length = ( A2DP_BLOCK_LENGTH_16 | + A2DP_BLOCK_LENGTH_12 | + A2DP_BLOCK_LENGTH_8 | + A2DP_BLOCK_LENGTH_4 ); + } + + if (sbc->bitpool > 250) + return -EINVAL; + else if (sbc->bitpool > 0) + sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; + else { + sbc_cap->min_bitpool = 2; + sbc_cap->max_bitpool = 250; + } + + return 0; +} + + static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) { struct device *dev; bdaddr_t bdaddr; + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct sbc_codec_cap sbc_cap; str2ba(pkt->device, &bdaddr); @@ -460,6 +578,12 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, else if (pkt->role == PKT_ROLE_HIFI) client->interface = g_strdup(AUDIO_SINK_INTERFACE); + if (cfg_to_caps(cfg, &sbc_cap) < 0) + goto failed; + + client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) goto failed; @@ -475,6 +599,7 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, goto failed; create_stream(dev, client); + return; failed: -- cgit From 41b71b230e0aa4b495c091e7d17c6b939ddbce79 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 29 Aug 2007 21:35:39 +0000 Subject: Fix the need to have all parameters. --- audio/a2dp.c | 6 +++--- audio/pcm_bluetooth.c | 27 ++++++++++++--------------- audio/unix.c | 35 ++++++++++------------------------- 3 files changed, 25 insertions(+), 43 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 5ecd8159..43b32ada 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -252,20 +252,20 @@ static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) { struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; + struct sbc_codec_cap sbc_cap, *acp_sbc; if (!setup) return FALSE; if (setup->media_codec) - memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); + memcpy(&sbc_cap, setup->media_codec->data, sizeof(*acp_sbc)); else { media_codec = avdtp_get_codec(rsep); if (!media_codec) return FALSE; select_sbc_params(&sbc_cap, - (struct sbc_codec_cap *) media_codec->data); + (struct sbc_codec_cap *) media_codec->data); } media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index c6dc0931..8ba18464 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -898,8 +898,10 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; snd_config_iterator_t i, next; - const char *addr, *pref, *mode, *allocation, *rate, *channels, - *subbands, *blocks, *bitpool; + const char *addr, *pref, *mode, *allocation, *rate, *subbands, + *blocks, *bitpool; + + cfg->channels = 2; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -930,8 +932,10 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) if (strcmp(pref, "auto") == 0) pkt->role = PKT_ROLE_AUTO; else if (strcmp(pref, "voice") == 0 || - strcmp(pref, "hfp") == 0) + strcmp(pref, "hfp") == 0) { pkt->role = PKT_ROLE_VOICE; + cfg->channels = 1; + } else if (strcmp(pref, "hifi") == 0 || strcmp(pref, "a2dp") == 0) pkt->role = PKT_ROLE_HIFI; @@ -948,30 +952,23 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) continue; } - if (strcmp(id, "channels") == 0) { - if (snd_config_get_string(n, &channels) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - cfg->channels = strtod(channels, NULL); - continue; - } - - if (strcmp(id, "channel_mode") == 0) { + if (strcmp(id, "mode") == 0) { if (snd_config_get_string(n, &mode) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(pref, "mono") == 0) + if (strcmp(pref, "mono") == 0) { + cfg->channels = 1; cfg->channel_mode = CFG_CHANNEL_MODE_MONO; + } else if (strcmp(pref, "dual") == 0) cfg->channel_mode = CFG_CHANNEL_MODE_DUAL_CHANNEL; else if (strcmp(pref, "stereo") == 0) cfg->channel_mode = CFG_CHANNEL_MODE_STEREO; else if (strcmp(pref, "joint") == 0) cfg->channel_mode = CFG_CHANNEL_MODE_JOINT_STEREO; + continue; } diff --git a/audio/unix.c b/audio/unix.c index ef8d6673..6239c203 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -469,12 +469,8 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) default: return -EINVAL; } - } else { - sbc_cap->frequency = ( A2DP_SAMPLING_FREQ_48000 | - A2DP_SAMPLING_FREQ_44100 | - A2DP_SAMPLING_FREQ_32000 | - A2DP_SAMPLING_FREQ_16000 ); - } + } else + sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; if (cfg->channel_mode > 0) { switch (cfg->channel_mode) { @@ -487,12 +483,8 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) default: return -EINVAL; } - } else { - sbc_cap->channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | - A2DP_CHANNEL_MODE_STEREO | - A2DP_CHANNEL_MODE_DUAL_CHANNEL | - A2DP_CHANNEL_MODE_MONO ); - } + } else + sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; if (sbc->allocation > 0) { switch (sbc->allocation) { @@ -504,8 +496,7 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) return -EINVAL; } } else - sbc_cap->allocation_method = ( A2DP_ALLOCATION_LOUDNESS | - A2DP_ALLOCATION_SNR ); + sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; if (sbc->subbands > 0) { switch (sbc->subbands) { @@ -519,7 +510,7 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) return -EINVAL; } } else - sbc_cap->subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + sbc_cap->subbands = A2DP_SUBBANDS_8; if (sbc->blocks > 0) { switch (sbc->blocks) { @@ -538,21 +529,15 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) default: return -EINVAL; } - } else { - sbc_cap->block_length = ( A2DP_BLOCK_LENGTH_16 | - A2DP_BLOCK_LENGTH_12 | - A2DP_BLOCK_LENGTH_8 | - A2DP_BLOCK_LENGTH_4 ); - } + } else + sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; if (sbc->bitpool > 250) return -EINVAL; else if (sbc->bitpool > 0) sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; - else { - sbc_cap->min_bitpool = 2; - sbc_cap->max_bitpool = 250; - } + else + sbc_cap->min_bitpool = 53; return 0; } -- cgit From 69e07f5a7b3934fac5bcf4c554e14e0d0b3cec47 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 29 Aug 2007 22:19:18 +0000 Subject: Fix memory issue. --- audio/a2dp.c | 5 +++-- audio/pcm_bluetooth.c | 8 ++++---- audio/unix.c | 2 ++ 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 43b32ada..61510313 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -252,13 +252,13 @@ static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, GSList **caps) { struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap, *acp_sbc; + struct sbc_codec_cap sbc_cap; if (!setup) return FALSE; if (setup->media_codec) - memcpy(&sbc_cap, setup->media_codec->data, sizeof(*acp_sbc)); + memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); else { media_codec = avdtp_get_codec(rsep); if (!media_codec) @@ -278,6 +278,7 @@ static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, *caps = g_slist_append(*caps, media_codec); + return TRUE; } diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 8ba18464..92a94fc0 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -948,7 +948,7 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - cfg->rate = strtod(rate, NULL); + cfg->rate = atoi(rate); continue; } @@ -991,7 +991,7 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - sbc->subbands = strtod(subbands, NULL); + sbc->subbands = atoi(subbands); continue; } @@ -1001,7 +1001,7 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - sbc->blocks = strtod(blocks, NULL); + sbc->blocks = atoi(blocks); continue; } @@ -1011,7 +1011,7 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - sbc->bitpool = strtod(bitpool, NULL); + sbc->bitpool = atoi(bitpool); continue; } diff --git a/audio/unix.c b/audio/unix.c index 6239c203..7fa2db05 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -449,6 +449,8 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) { struct ipc_codec_sbc *sbc = (void *) cfg->data; + memset(sbc_cap, 0, sizeof(struct sbc_codec_cap)); + sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; -- cgit From f803f3511095e17811c50ebc85971ddd40c55618 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 29 Aug 2007 23:44:03 +0000 Subject: Fix the ALSA plugin parameter mess --- audio/ipc.h | 40 +++++----- audio/pcm_bluetooth.c | 62 ++++++++------- audio/unix.c | 203 ++++++++++++++++++++++++-------------------------- 3 files changed, 150 insertions(+), 155 deletions(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 2043b761..1c26e304 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -60,36 +60,38 @@ struct ipc_packet { } __attribute__ ((packed)); /* File descriptor options */ -#define CFG_FD_OPT_READ 0 -#define CFG_FD_OPT_WRITE 1 -#define CFG_FD_OPT_READWRITE 2 +#define CFG_FD_OPT_READ 0 +#define CFG_FD_OPT_WRITE 1 +#define CFG_FD_OPT_READWRITE 2 /* Audio channel mode */ -#define CFG_CHANNEL_MODE_MONO (1 << 3) -#define CFG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define CFG_CHANNEL_MODE_STEREO (1 << 1) -#define CFG_CHANNEL_MODE_JOINT_STEREO 1 +#define CFG_MODE_AUTO 0 +#define CFG_MODE_MONO 1 +#define CFG_MODE_DUAL_CHANNEL 2 +#define CFG_MODE_STEREO 3 +#define CFG_MODE_JOINT_STEREO 4 + +/* Allocation method */ +#define CFG_ALLOCATION_AUTO 0 +#define CFG_ALLOCATION_LOUDNESS 1 +#define CFG_ALLOCATION_SNR 2 /* Codec options */ -#define CFG_CODEC_NONE 0 -#define CFG_CODEC_SBC 1 +#define CFG_CODEC_NONE 0 +#define CFG_CODEC_SCO 1 +#define CFG_CODEC_SBC 2 struct ipc_data_cfg { - uint8_t fd_opt; /* Stream file descriptor options: read, + uint8_t fd_opt; /* Stream file descriptor options: read, write or readwrite */ - uint8_t channels; /* Number of audio channel */ - uint8_t channel_mode; /* Audio channel mode*/ uint16_t pkt_len; /* Stream packet length */ - uint8_t sample_size; /* Sample size in bytes */ + uint8_t sample_size; /* Sample size in bytes */ + uint8_t mode; /* Audio channel mode */ uint16_t rate; /* Stream sample rate */ - uint8_t codec; /* Stream codec */ - uint8_t data[0]; /* Codec payload */ + uint8_t codec; /* Stream codec */ + uint8_t data[0]; /* Codec payload */ } __attribute__ ((packed)); -/* SBC codec options */ -#define CODEC_SBC_ALLOCATION_SNR (1 << 1) -#define CODEC_SBC_ALLOCATION_LOUDNESS 1 - struct ipc_codec_sbc { uint8_t allocation; uint8_t subbands; diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 92a94fc0..f712bb0f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -718,7 +718,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; - int err; + int err, channels; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -733,14 +733,15 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ + channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - cfg.channels, cfg.channels); + channels, channels); if (err < 0) return err; /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + cfg.rate, cfg.rate); if (err < 0) return err; @@ -751,7 +752,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 200); + 2, 200); if (err < 0) return err; @@ -773,7 +774,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; - int err; + int err, channels; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -788,14 +789,15 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ + channels = cfg.mode = CFG_MODE_MONO ? 1 : 2; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - cfg.channels, cfg.channels); + channels, channels); if (err < 0) return err; /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + cfg.rate, cfg.rate); if (err < 0) return err; @@ -806,7 +808,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 50); + 2, 50); if (err < 0) return err; @@ -874,9 +876,8 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, /* FIXME: init using flags? */ sbc_init(&a2dp->sbc, 0); a2dp->sbc.rate = cfg->rate; - a2dp->sbc.channels = cfg->channels; - if (cfg->channel_mode == CFG_CHANNEL_MODE_MONO || - cfg->channel_mode == CFG_CHANNEL_MODE_JOINT_STEREO) + a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; + if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) a2dp->sbc.joint = 1; a2dp->sbc.allocation = sbc->allocation; a2dp->sbc.subbands = sbc->subbands; @@ -898,10 +899,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; snd_config_iterator_t i, next; - const char *addr, *pref, *mode, *allocation, *rate, *subbands, - *blocks, *bitpool; - - cfg->channels = 2; + const char *addr, *pref; + const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -934,9 +933,7 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) else if (strcmp(pref, "voice") == 0 || strcmp(pref, "hfp") == 0) { pkt->role = PKT_ROLE_VOICE; - cfg->channels = 1; - } - else if (strcmp(pref, "hifi") == 0 || + } else if (strcmp(pref, "hifi") == 0 || strcmp(pref, "a2dp") == 0) pkt->role = PKT_ROLE_HIFI; continue; @@ -958,17 +955,16 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - if (strcmp(pref, "mono") == 0) { - cfg->channels = 1; - cfg->channel_mode = CFG_CHANNEL_MODE_MONO; - } + if (strcmp(pref, "auto") == 0) + cfg->mode = CFG_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->mode = CFG_MODE_MONO; else if (strcmp(pref, "dual") == 0) - cfg->channel_mode = CFG_CHANNEL_MODE_DUAL_CHANNEL; + cfg->mode = CFG_MODE_DUAL_CHANNEL; else if (strcmp(pref, "stereo") == 0) - cfg->channel_mode = CFG_CHANNEL_MODE_STEREO; + cfg->mode = CFG_MODE_STEREO; else if (strcmp(pref, "joint") == 0) - cfg->channel_mode = CFG_CHANNEL_MODE_JOINT_STEREO; - + cfg->mode = CFG_MODE_JOINT_STEREO; continue; } @@ -978,10 +974,12 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return -EINVAL; } - if (strcmp(pref, "snr") == 0) - sbc->allocation = CODEC_SBC_ALLOCATION_SNR; + if (strcmp(pref, "auto") == 0) + sbc->allocation = CFG_ALLOCATION_AUTO; else if (strcmp(pref, "loudness") == 0) - sbc->allocation = CODEC_SBC_ALLOCATION_LOUDNESS; + sbc->allocation = CFG_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + sbc->allocation = CFG_ALLOCATION_SNR; continue; } @@ -1093,9 +1091,9 @@ done: DBG("Device configuration:"); - DBG("\n\tfd=%d\n\tfd_opt=%u\n\tchannels=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - data->stream_fd, data->cfg.fd_opt, data->cfg.channels, - data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); + DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", + data->stream_fd, data->cfg.fd_opt, data->cfg.pkt_len, + data->cfg.sample_size, data->cfg.rate); if (data->cfg.codec == CFG_CODEC_SBC) { ret = bluetooth_a2dp_init(data, sbc); diff --git a/audio/unix.c b/audio/unix.c index 7fa2db05..3b9d6d46 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -209,9 +209,9 @@ static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) return len; } - debug("fd=%d, fd_opt=%u, channels=%u, pkt_len=%u," - "sample_size=%u, rate=%u", fd, cfg->fd_opt, cfg->channels, - cfg->pkt_len, cfg->sample_size, cfg->rate); + debug("fd=%d, fd_opt=%u, pkt_len=%u, sample_size=%u, rate=%u", + fd, cfg->fd_opt, cfg->pkt_len, + cfg->sample_size, cfg->rate); if (cfg->codec == CFG_CODEC_SBC) codec_len = sizeof(struct ipc_codec_sbc); @@ -258,9 +258,8 @@ static void headset_setup_complete(struct device *dev, void *user_data) memset(&cfg, 0, sizeof(cfg)); cfg.fd_opt = CFG_FD_OPT_READWRITE; - cfg.codec = CFG_CODEC_NONE; - cfg.channels = 1; - cfg.channel_mode = CFG_CHANNEL_MODE_MONO; + cfg.codec = CFG_CODEC_SCO; + cfg.mode = CFG_MODE_MONO; cfg.pkt_len = 48; cfg.sample_size = 2; cfg.rate = 8000; @@ -327,24 +326,22 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, cfg->fd_opt = CFG_FD_OPT_WRITE; sbc_cap = (void *) codec_cap; - cfg->channels = sbc_cap->channel_mode == A2DP_CHANNEL_MODE_MONO ? - 1 : 2; - cfg->channel_mode = sbc_cap->channel_mode; + cfg->mode = sbc_cap->channel_mode; cfg->sample_size = 2; switch (sbc_cap->frequency) { - case A2DP_SAMPLING_FREQ_16000: - cfg->rate = 16000; - break; - case A2DP_SAMPLING_FREQ_32000: - cfg->rate = 32000; - break; - case A2DP_SAMPLING_FREQ_44100: - cfg->rate = 44100; - break; - case A2DP_SAMPLING_FREQ_48000: - cfg->rate = 48000; - break; + case A2DP_SAMPLING_FREQ_16000: + cfg->rate = 16000; + break; + case A2DP_SAMPLING_FREQ_32000: + cfg->rate = 32000; + break; + case A2DP_SAMPLING_FREQ_44100: + cfg->rate = 44100; + break; + case A2DP_SAMPLING_FREQ_48000: + cfg->rate = 48000; + break; } cfg->codec = CFG_CODEC_SBC; @@ -353,18 +350,18 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; switch (sbc_cap->block_length) { - case A2DP_BLOCK_LENGTH_4: - sbc->blocks = 4; - break; - case A2DP_BLOCK_LENGTH_8: - sbc->blocks = 8; - break; - case A2DP_BLOCK_LENGTH_12: - sbc->blocks = 12; - break; - case A2DP_BLOCK_LENGTH_16: - sbc->blocks = 16; - break; + case A2DP_BLOCK_LENGTH_4: + sbc->blocks = 4; + break; + case A2DP_BLOCK_LENGTH_8: + sbc->blocks = 8; + break; + case A2DP_BLOCK_LENGTH_12: + sbc->blocks = 12; + break; + case A2DP_BLOCK_LENGTH_16: + sbc->blocks = 16; + break; } sbc->bitpool = sbc_cap->max_bitpool; @@ -454,92 +451,90 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; - if (cfg->rate > 0) { - switch (cfg->rate) { - case 48000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; - break; - case 44100: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; - break; - case 32000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; - break; - case 16000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; - break; - default: - return -EINVAL; - } - } else + switch (cfg->rate) { + case 48000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; + break; + case 44100: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; + break; + case 32000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; + break; + case 16000: + sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; + break; + default: sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; + break; + } - if (cfg->channel_mode > 0) { - switch (cfg->channel_mode) { - case A2DP_CHANNEL_MODE_JOINT_STEREO: - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - case A2DP_CHANNEL_MODE_MONO: - sbc_cap->channel_mode = cfg->channel_mode; - break; - default: - return -EINVAL; - } - } else + switch (cfg->mode) { + case CFG_MODE_MONO: + sbc_cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + break; + case CFG_MODE_DUAL_CHANNEL: + sbc_cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; + break; + case CFG_MODE_STEREO: + sbc_cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; + break; + case CFG_MODE_JOINT_STEREO: + sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; + break; + default: sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; + break; + } - if (sbc->allocation > 0) { - switch (sbc->allocation) { - case A2DP_ALLOCATION_LOUDNESS: - case A2DP_ALLOCATION_SNR: - sbc_cap->allocation_method = sbc->allocation; - break; - default: - return -EINVAL; - } - } else + switch (sbc->allocation) { + case CFG_ALLOCATION_LOUDNESS: + sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + break; + case CFG_ALLOCATION_SNR: + sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + break; + default: sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + break; + } - if (sbc->subbands > 0) { - switch (sbc->subbands) { - case 8: - sbc_cap->subbands = A2DP_SUBBANDS_8; - break; - case 4: - sbc_cap->subbands = A2DP_SUBBANDS_4; - break; - default: - return -EINVAL; - } - } else + switch (sbc->subbands) { + case 8: sbc_cap->subbands = A2DP_SUBBANDS_8; + break; + case 4: + sbc_cap->subbands = A2DP_SUBBANDS_4; + break; + default: + sbc_cap->subbands = A2DP_SUBBANDS_8; + break; + } - if (sbc->blocks > 0) { - switch (sbc->blocks) { - case 16: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; - break; - case 12: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; - break; - case 8: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; - break; - case 4: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; - break; - default: - return -EINVAL; - } - } else + switch (sbc->blocks) { + case 16: sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; + break; + case 12: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; + break; + case 8: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; + break; + case 4: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; + break; + default: + sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; + break; + } if (sbc->bitpool > 250) return -EINVAL; else if (sbc->bitpool > 0) sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; else - sbc_cap->min_bitpool = 53; + sbc_cap->min_bitpool = sbc_cap->max_bitpool = 53; return 0; } -- cgit From fcba092e96541bfca5d910c8aca1b2e31336bbbe Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 29 Aug 2007 23:48:08 +0000 Subject: Use a bitpool range as default --- audio/unix.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 3b9d6d46..ca59e5ab 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -533,8 +533,11 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) return -EINVAL; else if (sbc->bitpool > 0) sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; - else - sbc_cap->min_bitpool = sbc_cap->max_bitpool = 53; + else { + sbc->bitpool = 53; + sbc_cap->min_bitpool = 2; + sbc_cap->max_bitpool = sbc->bitpool; + } return 0; } -- cgit From f40848f9ef7ecdf101a32d7ec1f41c267e7756eb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 29 Aug 2007 23:54:05 +0000 Subject: Coding style fixes --- audio/pcm_bluetooth.c | 4 +--- audio/unix.c | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index f712bb0f..ff82bff1 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -334,8 +334,7 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, } static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, - unsigned int space) + struct pollfd *pfd, unsigned int space) { struct bluetooth_data *data = io->private_data; @@ -815,7 +814,6 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return 0; } - static int bluetooth_recvmsg_fd(struct bluetooth_data *data) { char cmsg_b[CMSG_SPACE(sizeof(int))]; diff --git a/audio/unix.c b/audio/unix.c index ca59e5ab..8cdb1323 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -97,8 +97,8 @@ static void client_free(struct unix_client *client) a2dp = &client->d.a2dp; if (client->cb_id > 0) avdtp_stream_remove_cb(a2dp->session, a2dp->stream, - client->cb_id); - if (a2dp->session); + client->cb_id); + if (a2dp->session) avdtp_unref(a2dp->session); break; default: @@ -107,8 +107,10 @@ static void client_free(struct unix_client *client) if (client->sock >= 0) close(client->sock); + if (client->media_codec) g_free(client->media_codec); + g_free(client->interface); g_free(client); } @@ -165,7 +167,6 @@ static service_type_t select_service(struct device *dev, const char *interface) return TYPE_NONE; } - static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, @@ -346,7 +347,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, cfg->codec = CFG_CODEC_SBC; sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? - 0x01 : 0x00; + 0x01 : 0x00; sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; switch (sbc_cap->block_length) { @@ -378,7 +379,9 @@ failed: if (a2dp->stream) a2dp_source_unlock(dev, session); unix_send_cfg(client->sock, NULL, -1); + avdtp_unref(a2dp->session); + a2dp->session = NULL; a2dp->stream = NULL; } @@ -408,11 +411,12 @@ static void create_stream(struct device *dev, struct unix_client *client) client->media_codec); client->cancel_stream = a2dp_source_cancel_stream; break; + case TYPE_HEADSET: - id = headset_request_stream(dev, headset_setup_complete, - client); + id = headset_request_stream(dev, headset_setup_complete, client); client->cancel_stream = headset_cancel_stream; break; + default: error("No known services for device"); goto failed; @@ -542,7 +546,6 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) return 0; } - static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) { @@ -566,8 +569,8 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, if (cfg_to_caps(cfg, &sbc_cap) < 0) goto failed; - client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); + client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, + &sbc_cap, sizeof(sbc_cap)); if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) @@ -591,13 +594,13 @@ failed: unix_send_cfg(client->sock, NULL, -1); } -static void ctl_event(struct unix_client *client, struct ipc_packet *pkt, - int len) +static void ctl_event(struct unix_client *client, + struct ipc_packet *pkt, int len) { } -static void state_event(struct unix_client *client, struct ipc_packet *pkt, - int len) +static void state_event(struct unix_client *client, + struct ipc_packet *pkt, int len) { #if 0 struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; @@ -719,7 +722,7 @@ static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) io = g_io_channel_unix_new(cli_sk); g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - client_cb, client); + client_cb, client); g_io_channel_unref(io); return TRUE; @@ -755,7 +758,7 @@ int unix_init(void) io = g_io_channel_unix_new(sk); g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - server_cb, NULL); + server_cb, NULL); g_io_channel_unref(io); info("Unix socket created: %d", sk); -- cgit From 31bd0bed86998f29e89061e5a3b0925c0d2f52a5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 30 Aug 2007 00:02:37 +0000 Subject: Change the default bitpool range to 2-32 --- audio/unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 8cdb1323..5d049dbc 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -538,7 +538,7 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) else if (sbc->bitpool > 0) sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; else { - sbc->bitpool = 53; + sbc->bitpool = 32; sbc_cap->min_bitpool = 2; sbc_cap->max_bitpool = sbc->bitpool; } -- cgit From 4183300934043b1038c84652848c271a235bc5df Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 30 Aug 2007 00:45:53 +0000 Subject: Disable the broken optional codec parameters setup --- audio/a2dp.c | 10 +++++----- audio/unix.c | 19 +++++++++---------- 2 files changed, 14 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 61510313..dafb608a 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -240,7 +240,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, min_bitpool = MAX(2, supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), - supported->max_bitpool); + supported->max_bitpool); cap->min_bitpool = min_bitpool; cap->max_bitpool = max_bitpool; @@ -257,9 +257,9 @@ static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, if (!setup) return FALSE; - if (setup->media_codec) + if (setup->media_codec) { memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); - else { + } else { media_codec = avdtp_get_codec(rsep); if (!media_codec) return FALSE; @@ -311,7 +311,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, } err = avdtp_set_configuration(session, rsep, lsep, caps, - &setup->stream); + &setup->stream); if (err < 0) { error("avdtp_set_configuration: %s", strerror(-err)); finalize_stream_setup(setup); @@ -1045,7 +1045,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, break; if (setup->media_codec) { if (avdtp_stream_has_capability(setup->stream, - setup->media_codec)) { + setup->media_codec)) { if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; diff --git a/audio/unix.c b/audio/unix.c index 5d049dbc..d980861f 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -405,10 +405,12 @@ static void create_stream(struct device *dev, struct unix_client *client) goto failed; } + /* FIXME: The provided media_codec breaks bitpool + selection. So disable it. This needs fixing */ id = a2dp_source_request_stream(a2dp->session, dev, TRUE, a2dp_setup_complete, client, &a2dp->sep, - client->media_codec); + NULL/*client->media_codec*/); client->cancel_stream = a2dp_source_cancel_stream; break; @@ -533,21 +535,18 @@ static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) break; } - if (sbc->bitpool > 250) - return -EINVAL; - else if (sbc->bitpool > 0) - sbc_cap->min_bitpool = sbc_cap->max_bitpool = sbc->bitpool; - else { - sbc->bitpool = 32; - sbc_cap->min_bitpool = 2; + if (sbc->bitpool != 0) { + if (sbc->bitpool > 250) + return -EINVAL; + + sbc_cap->min_bitpool = sbc->bitpool; sbc_cap->max_bitpool = sbc->bitpool; } return 0; } -static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, - int len) +static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) { struct device *dev; bdaddr_t bdaddr; -- cgit From 734b3f84c1e48f241bf1004e57a86fa898d083a5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 30 Aug 2007 00:50:23 +0000 Subject: Prefer 44.1kHz over 48kHz --- audio/a2dp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index dafb608a..aea7224a 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -185,10 +185,10 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; cap->cap.media_codec_type = A2DP_CODEC_SBC; - if (supported->frequency & A2DP_SAMPLING_FREQ_48000) - cap->frequency = A2DP_SAMPLING_FREQ_48000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_44100) + if (supported->frequency & A2DP_SAMPLING_FREQ_44100) cap->frequency = A2DP_SAMPLING_FREQ_44100; + else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) + cap->frequency = A2DP_SAMPLING_FREQ_48000; else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) cap->frequency = A2DP_SAMPLING_FREQ_32000; else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) -- cgit From 921c1a63009394047b963930cfd49fea60017599 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Aug 2007 08:12:20 +0000 Subject: Fix behaviour when no adapter is connected --- audio/manager.c | 9 ++++++++- audio/manager.h | 2 +- audio/unix.c | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 9c57d17d..30b78632 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -608,6 +608,8 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) device = manager_find_device(bda, NULL, FALSE); if (!device) { device = create_device(bda); + if (!device) + return NULL; if (!add_device(device, TRUE)) { remove_device(device); return NULL; @@ -676,13 +678,18 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) return device; } -void manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, +gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data) { struct device *dev; dev = create_device(bda); + if (!dev) + return FALSE; + resolve_services(NULL, dev, cb, user_data); + + return TRUE; } static DBusHandlerResult am_create_device(DBusConnection *conn, diff --git a/audio/manager.h b/audio/manager.h index ceabe680..e71e5d62 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -49,7 +49,7 @@ struct device *manager_find_device(bdaddr_t *bda, const char *interface, struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); -void manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, +gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data); gboolean manager_authorize(bdaddr_t *dba, const char *uuid, diff --git a/audio/unix.c b/audio/unix.c index d980861f..cee56f8a 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -574,7 +574,8 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) goto failed; - manager_create_device(&bdaddr, create_cb, client); + if (!manager_create_device(&bdaddr, create_cb, client)) + goto failed; return; } -- cgit From 67d1e1774b3ff2d7a09fcf49484515bd85c02d14 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Aug 2007 09:13:51 +0000 Subject: Fix stream setup error handling --- audio/a2dp.c | 7 +++---- audio/sink.c | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index aea7224a..45a1bfe0 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -315,11 +315,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, if (err < 0) { error("avdtp_set_configuration: %s", strerror(-err)); finalize_stream_setup(setup); - return; } - - /* Notify sink.c of the new stream */ - sink_new_stream(setup->dev, session, setup->stream); } static gboolean setconf_ind(struct avdtp *session, @@ -434,6 +430,9 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (!setup) return; + /* Notify sink.c of the new stream */ + sink_new_stream(setup->dev, session, setup->stream); + ret = avdtp_open(session, stream); if (ret < 0) { error("Error on avdtp_open %s (%d)", strerror(-ret), diff --git a/audio/sink.c b/audio/sink.c index 599fdaaf..67c53ccd 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -144,6 +144,7 @@ static void stream_setup_complete(struct avdtp *session, struct device *dev, pending = sink->connect; sink->connect = NULL; + sink->cb_id = 0; if (stream) { DBusMessage *reply; -- cgit From fbcf118559e6e8c2b593d39931810942d61dcd00 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Aug 2007 09:23:04 +0000 Subject: Don't confuse stream callback id's and stream setup callback id's --- audio/sink.c | 1 - 1 file changed, 1 deletion(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 67c53ccd..599fdaaf 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -144,7 +144,6 @@ static void stream_setup_complete(struct avdtp *session, struct device *dev, pending = sink->connect; sink->connect = NULL; - sink->cb_id = 0; if (stream) { DBusMessage *reply; -- cgit From 000367f802b2ea5e6a06a009eaaaa1463ec0026f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 30 Aug 2007 14:45:51 +0000 Subject: Remove unused variables --- audio/sink.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'audio') diff --git a/audio/sink.c b/audio/sink.c index 599fdaaf..285dc45c 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -57,8 +57,6 @@ struct sink { struct pending_request *connect; struct pending_request *disconnect; DBusConnection *conn; - gboolean initiator; - gboolean suspending; }; static void pending_request_free(struct pending_request *pending) @@ -340,7 +338,6 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, sink->session = avdtp_ref(session); sink->stream = stream; - sink->initiator = FALSE; sink->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, dev); -- cgit From 332157202da1e230b900a0bf72153177c5da0be6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 30 Aug 2007 20:24:19 +0000 Subject: Fix missing {}. --- audio/pcm_bluetooth.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index ff82bff1..9fed93a8 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -846,10 +846,11 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) + && cmsg->cmsg_type == SCM_RIGHTS) { data->stream_fd = (*(int *) CMSG_DATA(cmsg)); DBG("stream_fd=%d", data->stream_fd); return 0; + } } } else SNDERR("Unexpected packet type %d received", pkt.type); -- cgit From b1df213042ef14c24b6d370e5fe16c681508af8b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 30 Aug 2007 21:36:25 +0000 Subject: Fix use of = instead of == to compare audio mode. --- audio/pcm_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 9fed93a8..b99f4ccd 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -788,7 +788,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - channels = cfg.mode = CFG_MODE_MONO ? 1 : 2; + channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, channels, channels); if (err < 0) -- cgit From 828133c3180a090a06ace6637d9f84ae5f57ae33 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 06:21:52 +0000 Subject: Fix local SEP locking when connecting fails or unix client disconnects --- audio/a2dp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 45a1bfe0..1505b798 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -75,6 +75,7 @@ struct a2dp_stream_cb { struct a2dp_stream_setup { struct avdtp *session; struct device *dev; + struct a2dp_sep *sep; struct avdtp_stream *stream; struct avdtp_service_capability *media_codec; gboolean start; @@ -110,6 +111,8 @@ static void setup_callback(struct a2dp_stream_cb *cb, static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) { + if (!s->stream) + s->sep->used_by = NULL; g_slist_foreach(setup->cb, (GFunc) setup_callback, setup); stream_setup_free(setup); return FALSE; @@ -289,7 +292,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, struct avdtp_remote_sep *rsep; GSList *caps = NULL; - if (err < 0) { + if (err < 0 || setup->canceled) { setup->stream = NULL; finalize_stream_setup(setup); return; @@ -961,8 +964,10 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) setup->cb = g_slist_remove(setup->cb, cb_data); g_free(cb_data); - if (!setup->cb) + if (!setup->cb) { setup->canceled = TRUE; + setup->sep->used_by = NULL; + } return TRUE; } @@ -1022,6 +1027,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup = g_new0(struct a2dp_stream_setup, 1); setup->session = avdtp_ref(session); + setup->sep = sep; setup->dev = dev; setup->cb = g_slist_append(setup->cb, cb_data); setup->start = start; -- cgit From 03490d81bbe11641c47cd8e7e0c64a45db4f80f4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 08:28:22 +0000 Subject: Fix more SEP locking issues --- audio/a2dp.c | 102 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 26 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 1505b798..689e9da6 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -61,7 +61,6 @@ struct a2dp_sep { struct device *used_by; guint suspend_timer; gboolean locked; - gboolean start_requested; gboolean suspending; gboolean starting; }; @@ -91,16 +90,16 @@ static GSList *sources = NULL; static uint32_t source_record_id = 0; static uint32_t sink_record_id = 0; -static struct a2dp_stream_setup *setup = NULL; +static GSList *setups = NULL; static void stream_setup_free(struct a2dp_stream_setup *s) { + setups = g_slist_remove(setups, s); if (s->session) avdtp_unref(s->session); g_slist_foreach(s->cb, (GFunc) g_free, NULL); g_slist_free(s->cb); g_free(s); - setup = NULL; } static void setup_callback(struct a2dp_stream_cb *cb, @@ -111,13 +110,41 @@ static void setup_callback(struct a2dp_stream_cb *cb, static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) { - if (!s->stream) + if (!s->stream && s->sep) s->sep->used_by = NULL; - g_slist_foreach(setup->cb, (GFunc) setup_callback, setup); - stream_setup_free(setup); + g_slist_foreach(s->cb, (GFunc) setup_callback, s); + stream_setup_free(s); return FALSE; } +static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session) +{ + GSList *l; + + for (l = setups; l != NULL; l = l->next) { + struct a2dp_stream_setup *setup = l->data; + + if (setup->session == session) + return setup; + } + + return NULL; +} + +static struct a2dp_stream_setup *find_setup_by_dev(struct device *dev) +{ + GSList *l; + + for (l = setups; l != NULL; l = l->next) { + struct a2dp_stream_setup *setup = l->data; + + if (setup->dev == dev) + return setup; + } + + return NULL; +} + static void stream_state_changed(struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, @@ -251,12 +278,15 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, return TRUE; } -static gboolean a2dp_select_capabilities(struct avdtp_remote_sep *rsep, +static gboolean a2dp_select_capabilities(struct avdtp *session, + struct avdtp_remote_sep *rsep, GSList **caps) { struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; + struct a2dp_stream_setup *setup; + setup = find_setup_by_session(session); if (!setup) return FALSE; @@ -290,8 +320,14 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, { struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; + struct a2dp_stream_setup *setup; GSList *caps = NULL; + setup = find_setup_by_session(session); + + if (!setup) + return; + if (err < 0 || setup->canceled) { setup->stream = NULL; finalize_stream_setup(setup); @@ -307,7 +343,7 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, return; } - if (!a2dp_select_capabilities(rsep, &caps)) { + if (!a2dp_select_capabilities(session, rsep, &caps)) { error("Unable to select remote SEP capabilities"); finalize_stream_setup(setup); return; @@ -347,6 +383,7 @@ static gboolean setconf_ind(struct avdtp *session, avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; + a2dp_sep->used_by = dev; if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) sink_new_stream(dev, session, stream); @@ -414,6 +451,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) @@ -421,6 +459,8 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, else debug("SBC Source: Set_Configuration_Cfm"); + setup = find_setup_by_session(session); + if (err) { if (setup) finalize_stream_setup(setup); @@ -487,12 +527,14 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Cfm"); else debug("SBC Source: Open_Cfm"); + setup = find_setup_by_session(session); if (!setup) return; @@ -557,12 +599,14 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Cfm"); else debug("SBC Source: Start_Cfm"); + setup = find_setup_by_session(session); if (!setup) return; @@ -597,6 +641,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); @@ -605,16 +650,18 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, a2dp_sep->suspending = FALSE; + setup = find_setup_by_session(session); + if (!setup) + return; + if (err) { - a2dp_sep->start_requested = FALSE; - if (setup) - finalize_stream_setup(setup); + finalize_stream_setup(setup); return; } - if (a2dp_sep->start_requested) { - avdtp_start(session, stream); - a2dp_sep->start_requested = FALSE; + if (setup->start) { + if (avdtp_start(session, stream) < 0) + finalize_stream_setup(setup); } } @@ -637,12 +684,14 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); else debug("SBC Source: Close_Cfm"); + setup = find_setup_by_session(session); if (!setup) return; @@ -679,6 +728,8 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, else debug("SBC Source: Abort_Ind"); + a2dp_sep->used_by = NULL; + return TRUE; } @@ -711,12 +762,14 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_stream_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); + setup = find_setup_by_session(session); if (!setup) return; @@ -944,8 +997,10 @@ void a2dp_exit() gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) { struct a2dp_stream_cb *cb_data; + struct a2dp_stream_setup *setup; GSList *l; + setup = find_setup_by_dev(dev); if (!setup) return FALSE; @@ -967,6 +1022,7 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) if (!setup->cb) { setup->canceled = TRUE; setup->sep->used_by = NULL; + setup->sep = NULL; } return TRUE; @@ -983,6 +1039,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, struct a2dp_stream_cb *cb_data; static unsigned int cb_id = 0; GSList *l; + struct a2dp_stream_setup *setup; struct a2dp_sep *sep = NULL; for (l = sources; l != NULL; l = l->next) { @@ -1002,11 +1059,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, return 0; } - if (setup && setup->dev != dev) { - error("a2dp_source_request_stream: stream setup in progress " - "already for another device"); - return 0; - } + setup = find_setup_by_session(session); sep->used_by = dev; @@ -1019,6 +1072,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, if (setup) { setup->canceled = FALSE; + setup->sep = sep; setup->cb = g_slist_append(setup->cb, cb_data); if (start) setup->start = TRUE; @@ -1073,14 +1127,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, g_source_remove(sep->suspend_timer); sep->suspend_timer = 0; } - if (sep->session) { - avdtp_unref(sep->session); - sep->session = NULL; - } - g_idle_add((GSourceFunc) finalize_stream_setup, setup); - return cb_data->id; } - sep->start_requested = TRUE; + g_idle_add((GSourceFunc) finalize_stream_setup, setup); break; default: error("SEP in bad state for requesting a new stream"); @@ -1090,6 +1138,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, if (ret) *ret = sep; + setups = g_slist_append(setups, setup); + return cb_data->id; failed: -- cgit From fc0d501d82773718d0f2d040f786136332c39813 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 10:25:18 +0000 Subject: Cleanup and fixes of stream handling --- audio/a2dp.c | 88 ++++++++++++++---------------------------- audio/a2dp.h | 8 ++-- audio/avdtp.c | 117 ++++++++++++++++++++++++++++++++++---------------------- audio/avdtp.h | 2 + audio/headset.c | 4 +- audio/headset.h | 4 +- audio/sink.c | 8 ++-- audio/unix.c | 71 ++++++++++++++++------------------ 8 files changed, 146 insertions(+), 156 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 689e9da6..fa8d1175 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -58,7 +58,6 @@ struct a2dp_sep { struct avdtp_local_sep *sep; struct avdtp *session; struct avdtp_stream *stream; - struct device *used_by; guint suspend_timer; gboolean locked; gboolean suspending; @@ -73,7 +72,6 @@ struct a2dp_stream_cb { struct a2dp_stream_setup { struct avdtp *session; - struct device *dev; struct a2dp_sep *sep; struct avdtp_stream *stream; struct avdtp_service_capability *media_codec; @@ -102,16 +100,23 @@ static void stream_setup_free(struct a2dp_stream_setup *s) g_free(s); } +static struct device *a2dp_get_dev(struct avdtp *session) +{ + bdaddr_t addr; + + avdtp_get_peers(session, NULL, &addr); + + return manager_device_connected(&addr, A2DP_SOURCE_UUID); +} + static void setup_callback(struct a2dp_stream_cb *cb, struct a2dp_stream_setup *s) { - cb->cb(s->session, s->dev, s->stream, cb->user_data); + cb->cb(s->session, s->sep, s->stream, cb->user_data); } static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) { - if (!s->stream && s->sep) - s->sep->used_by = NULL; g_slist_foreach(s->cb, (GFunc) setup_callback, s); stream_setup_free(s); return FALSE; @@ -137,8 +142,9 @@ static struct a2dp_stream_setup *find_setup_by_dev(struct device *dev) for (l = setups; l != NULL; l = l->next) { struct a2dp_stream_setup *setup = l->data; + struct device *setup_dev = a2dp_get_dev(setup->session); - if (setup->dev == dev) + if (setup_dev == dev) return setup; } @@ -365,16 +371,13 @@ static gboolean setconf_ind(struct avdtp *session, { struct a2dp_sep *a2dp_sep = user_data; struct device *dev; - bdaddr_t addr; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Ind"); else debug("SBC Source: Set_Configuration_Ind"); - avdtp_get_peers(session, NULL, &addr); - - dev = manager_device_connected(&addr, A2DP_SOURCE_UUID); + dev = a2dp_get_dev(session); if (!dev) { *err = AVDTP_UNSUPPORTED_CONFIGURATION; *category = 0x00; @@ -383,7 +386,6 @@ static gboolean setconf_ind(struct avdtp *session, avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; - a2dp_sep->used_by = dev; if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) sink_new_stream(dev, session, stream); @@ -452,6 +454,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_stream_setup *setup; + struct device *dev; int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) @@ -473,8 +476,10 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (!setup) return; + dev = a2dp_get_dev(session); + /* Notify sink.c of the new stream */ - sink_new_stream(setup->dev, session, setup->stream); + sink_new_stream(dev, session, setup->stream); ret = avdtp_open(session, stream); if (ret < 0) { @@ -728,7 +733,7 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, else debug("SBC Source: Abort_Ind"); - a2dp_sep->used_by = NULL; + a2dp_sep->stream = NULL; return TRUE; } @@ -1021,7 +1026,6 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) if (!setup->cb) { setup->canceled = TRUE; - setup->sep->used_by = NULL; setup->sep = NULL; } @@ -1029,11 +1033,9 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) } unsigned int a2dp_source_request_stream(struct avdtp *session, - struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data, - struct a2dp_sep **ret, struct avdtp_service_capability *media_codec) { struct a2dp_stream_cb *cb_data; @@ -1048,7 +1050,7 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, if (tmp->locked) continue; - if (tmp->used_by == NULL || tmp->used_by == dev) { + if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) { sep = tmp; break; } @@ -1061,8 +1063,6 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup = find_setup_by_session(session); - sep->used_by = dev; - debug("a2dp_source_request_stream: selected SEP %p", sep); cb_data = g_new(struct a2dp_stream_cb, 1); @@ -1082,7 +1082,6 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, setup = g_new0(struct a2dp_stream_setup, 1); setup->session = avdtp_ref(session); setup->sep = sep; - setup->dev = dev; setup->cb = g_slist_append(setup->cb, cb_data); setup->start = start; setup->stream = sep->stream; @@ -1135,9 +1134,6 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, goto failed; } - if (ret) - *ret = sep; - setups = g_slist_append(setups, setup); return cb_data->id; @@ -1148,52 +1144,24 @@ failed: return 0; } -gboolean a2dp_source_lock(struct device *dev, struct avdtp *session) +gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session) { - GSList *l; - - for (l = sources; l != NULL; l = l->next) { - struct a2dp_sep *sep = l->data; - - if (sep->locked) - continue; - - if (sep->used_by != dev) - continue; + if (sep->locked) + return FALSE; - debug("SBC Source SEP %p locked", sep); - sep->locked = TRUE; - return TRUE; - } + debug("SBC Source SEP %p locked", sep); + sep->locked = TRUE; - return FALSE; + return TRUE; } -gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session) +gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) { avdtp_state_t state; - GSList *l; - struct a2dp_sep *sep = NULL; - - for (l = sources; l != NULL; l = l->next) { - struct a2dp_sep *tmp = l->data; - - if (!tmp->locked) - continue; - - if (tmp->sep && tmp->used_by == dev) { - sep = tmp; - break; - } - } - - if (!sep) - return FALSE; state = avdtp_sep_get_state(sep->sep); sep->locked = FALSE; - sep->used_by = NULL; debug("SBC Source SEP %p unlocked", sep); @@ -1224,7 +1192,7 @@ gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; - if (tmp->sep && tmp->used_by == dev) { + if (tmp->session && tmp->session == session) { sep = tmp; break; } @@ -1255,7 +1223,7 @@ gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; - if (tmp->sep && tmp->used_by == dev) { + if (tmp->session && tmp->session == session) { sep = tmp; break; } diff --git a/audio/a2dp.h b/audio/a2dp.h index ede0c70b..6a966302 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -60,7 +60,7 @@ struct sbc_codec_cap { struct a2dp_sep; -typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct device *dev, +typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, void *user_data); @@ -68,14 +68,12 @@ int a2dp_init(DBusConnection *conn, int sources, int sinks); void a2dp_exit(void); unsigned int a2dp_source_request_stream(struct avdtp *session, - struct device *dev, gboolean start, a2dp_stream_cb_t cb, void *user_data, - struct a2dp_sep **sep, struct avdtp_service_capability *media_codec); gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id); -gboolean a2dp_source_lock(struct device *dev, struct avdtp *session); -gboolean a2dp_source_unlock(struct device *dev, struct avdtp *session); +gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session); +gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session); gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session); gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session); diff --git a/audio/avdtp.c b/audio/avdtp.c index d03afbae..a50b82b3 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -387,6 +387,9 @@ static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_header *header, int size); static int process_queue(struct avdtp *session); static void connection_lost(struct avdtp *session, int err); +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state); static const char *avdtp_statestr(avdtp_state_t state) { @@ -572,6 +575,60 @@ static gboolean stream_timeout(struct avdtp_stream *stream) return FALSE; } +static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp_stream *stream = data; + struct avdtp_local_sep *sep = stream->lsep; + + if (stream->close_int && sep->cfm && sep->cfm->close) + sep->cfm->close(stream->session, sep, stream, NULL, + sep->user_data); + + stream->io = 0; + + avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); + + return FALSE; +} + +static void handle_transport_connect(struct avdtp *session, int sock, + uint16_t mtu) +{ + struct avdtp_stream *stream = session->pending_open; + struct avdtp_local_sep *sep = stream->lsep; + GIOChannel *channel; + + session->pending_open = NULL; + + if (stream->timer) { + g_source_remove(stream->timer); + stream->timer = 0; + } + + if (sock < 0) { + if (!stream->open_acp && sep->cfm && sep->cfm->open) { + struct avdtp_error err; + avdtp_error_init(&err, AVDTP_ERROR_ERRNO, EIO); + sep->cfm->open(session, sep, NULL, &err, + sep->user_data); + } + return; + } + + stream->sock = sock; + stream->mtu = mtu; + + if (!stream->open_acp && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, NULL, sep->user_data); + + channel = g_io_channel_unix_new(stream->sock); + + stream->io = g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) transport_cb, stream); + g_io_channel_unref(channel); +} + static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state) @@ -623,6 +680,8 @@ static void avdtp_sep_set_state(struct avdtp *session, stream->idle_timer = 0; } session->streams = g_slist_remove(session->streams, stream); + if (session->pending_open == stream) + handle_transport_connect(session, -1, 0); stream_free(stream); if (session->ref == 1 && !session->streams) set_disconnect_timer(session); @@ -1311,50 +1370,6 @@ static gboolean avdtp_parse_cmd(struct avdtp *session, } } -static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) -{ - struct avdtp_stream *stream = data; - struct avdtp_local_sep *sep = stream->lsep; - - if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(stream->session, sep, stream, NULL, - sep->user_data); - - stream->io = 0; - - avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); - - return FALSE; -} - -static void handle_transport_connect(struct avdtp *session, int sock, - uint16_t mtu) -{ - struct avdtp_stream *stream = session->pending_open; - struct avdtp_local_sep *sep = stream->lsep; - GIOChannel *channel; - - session->pending_open = NULL; - - if (stream->timer) { - g_source_remove(stream->timer); - stream->timer = 0; - } - - stream->sock = sock; - stream->mtu = mtu; - - if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, NULL, sep->user_data); - - channel = g_io_channel_unix_new(stream->sock); - - stream->io = g_io_add_watch(channel, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) transport_cb, stream); - g_io_channel_unref(channel); -} - static void init_request(struct avdtp_header *header, int request_id) { static int transaction = 0; @@ -1488,12 +1503,13 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } + sk = g_io_channel_unix_get_fd(chan); + if (cond & (G_IO_ERR | G_IO_HUP)) { err = EIO; goto failed; } - sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { err = errno; @@ -1533,12 +1549,18 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, } else if (session->pending_open) handle_transport_connect(session, sk, l2o.imtu); + else { + err = -EIO; + goto failed; + } process_queue(session); return FALSE; failed: + close(sk); + if (session->pending_open) { avdtp_sep_set_state(session, session->pending_open->lsep, AVDTP_STATE_IDLE); @@ -2897,3 +2919,8 @@ void avdtp_exit(void) g_io_channel_unref(avdtp_server); avdtp_server = NULL; } + +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) +{ + return g_slist_find(session->streams, stream) ? TRUE : FALSE; +} diff --git a/audio/avdtp.h b/audio/avdtp.h index d429b435..d8408ac0 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -179,6 +179,8 @@ struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); + unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); diff --git a/audio/headset.c b/audio/headset.c index 5a626c17..25a5ebc6 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1604,7 +1604,7 @@ gboolean headset_is_active(struct device *dev) return FALSE; } -gboolean headset_lock(struct device *dev, void *data) +gboolean headset_lock(struct device *dev) { struct headset *hs = dev->headset; @@ -1616,7 +1616,7 @@ gboolean headset_lock(struct device *dev, void *data) return TRUE; } -gboolean headset_unlock(struct device *dev, void *data) +gboolean headset_unlock(struct device *dev) { struct headset *hs = dev->headset; diff --git a/audio/headset.h b/audio/headset.h index 3f762815..6824e0ce 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -74,7 +74,7 @@ int headset_get_sco_fd(struct device *dev); gboolean headset_is_active(struct device *dev); -gboolean headset_lock(struct device *dev, void *data); -gboolean headset_unlock(struct device *dev, void *data); +gboolean headset_lock(struct device *dev); +gboolean headset_unlock(struct device *dev); gboolean headset_suspend(struct device *dev, void *data); gboolean headset_play(struct device *dev, void *data); diff --git a/audio/sink.c b/audio/sink.c index 285dc45c..ea95bcf7 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -133,11 +133,11 @@ static void stream_state_changed(struct avdtp_stream *stream, sink->state = new_state; } -static void stream_setup_complete(struct avdtp *session, struct device *dev, +static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, void *user_data) { - struct sink *sink = dev->sink; + struct sink *sink = user_data; struct pending_request *pending; pending = sink->connect; @@ -185,8 +185,8 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, pending->msg = dbus_message_ref(msg); sink->connect = pending; - id = a2dp_source_request_stream(sink->session, dev, FALSE, - stream_setup_complete, pending, NULL, + id = a2dp_source_request_stream(sink->session, FALSE, + stream_setup_complete, sink, NULL); if (id == 0) { pending_request_free(pending); diff --git a/audio/unix.c b/audio/unix.c index cee56f8a..3e8c7469 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -66,7 +66,6 @@ struct a2dp_data { struct unix_client { struct device *dev; - struct avdtp_local_sep *sep; struct avdtp_service_capability *media_codec; service_type_t type; char *interface; @@ -77,9 +76,6 @@ struct unix_client { int sock; unsigned int req_id; unsigned int cb_id; - notify_cb_t disconnect; - notify_cb_t suspend; - notify_cb_t play; gboolean (*cancel_stream) (struct device *dev, unsigned int id); }; @@ -98,6 +94,8 @@ static void client_free(struct unix_client *client) if (client->cb_id > 0) avdtp_stream_remove_cb(a2dp->session, a2dp->stream, client->cb_id); + if (a2dp->sep) + a2dp_sep_unlock(a2dp->sep, a2dp->session); if (a2dp->session) avdtp_unref(a2dp->session); break; @@ -178,7 +176,10 @@ static void stream_state_changed(struct avdtp_stream *stream, switch (new_state) { case AVDTP_STATE_IDLE: - a2dp_source_unlock(client->dev, a2dp->session); + if (a2dp->sep) { + a2dp_sep_unlock(a2dp->sep, a2dp->session); + a2dp->sep = NULL; + } client->dev = NULL; if (a2dp->session) { avdtp_unref(a2dp->session); @@ -254,7 +255,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) return; } - headset_lock(dev, NULL); + headset_lock(dev); memset(&cfg, 0, sizeof(cfg)); @@ -268,13 +269,9 @@ static void headset_setup_complete(struct device *dev, void *user_data) fd = headset_get_sco_fd(dev); unix_send_cfg(client->sock, &cfg, fd); - - client->disconnect = (notify_cb_t) headset_unlock; - client->suspend = (notify_cb_t) headset_suspend; - client->play = (notify_cb_t) headset_play; } -static void a2dp_setup_complete(struct avdtp *session, struct device *dev, +static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, void *user_data) { @@ -294,15 +291,12 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, if (!stream) goto failed; - if (!a2dp_source_lock(dev, session)) { + if (!a2dp_sep_lock(sep, session)) { error("Unable to lock A2DP source SEP"); goto failed; } - client->disconnect = (notify_cb_t) a2dp_source_unlock; - client->suspend = (notify_cb_t) a2dp_source_suspend; - client->play = (notify_cb_t) a2dp_source_start_stream; - + a2dp->sep = sep; a2dp->stream = stream; if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { @@ -376,8 +370,10 @@ static void a2dp_setup_complete(struct avdtp *session, struct device *dev, failed: error("stream setup failed"); - if (a2dp->stream) - a2dp_source_unlock(dev, session); + if (a2dp->sep) { + a2dp_sep_unlock(a2dp->sep, a2dp->session); + a2dp->sep = NULL; + } unix_send_cfg(client->sock, NULL, -1); avdtp_unref(a2dp->session); @@ -407,9 +403,9 @@ static void create_stream(struct device *dev, struct unix_client *client) /* FIXME: The provided media_codec breaks bitpool selection. So disable it. This needs fixing */ - id = a2dp_source_request_stream(a2dp->session, dev, + id = a2dp_source_request_stream(a2dp->session, TRUE, a2dp_setup_complete, - client, &a2dp->sep, + client, NULL/*client->media_codec*/); client->cancel_stream = a2dp_source_cancel_stream; break; @@ -621,30 +617,29 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) struct ipc_packet *pkt = (void *) buf; struct unix_client *client = data; int len, len_check; - void *cb_data; + struct a2dp_data *a2dp = &client->d.a2dp; if (cond & G_IO_NVAL) return FALSE; - switch (client->type) { - case TYPE_HEADSET: - cb_data = client->d.data; - break; - case TYPE_SINK: - case TYPE_SOURCE: - cb_data = client->d.a2dp.session; - break; - default: - cb_data = NULL; - break; - } - if (cond & (G_IO_HUP | G_IO_ERR)) { debug("Unix client disconnected (fd=%d)", client->sock); - if (!client->dev) - goto failed; - if (client->disconnect) - client->disconnect(client->dev, cb_data); + switch (client->type) { + case TYPE_HEADSET: + if (client->dev) + headset_unlock(client->dev); + break; + case TYPE_SOURCE: + case TYPE_SINK: + if (a2dp->sep) { + a2dp_sep_unlock(a2dp->sep, a2dp->session); + a2dp->sep = NULL; + } + break; + default: + break; + } + if (client->cancel_stream && client->req_id > 0) client->cancel_stream(client->dev, client->req_id); goto failed; -- cgit From ec384afacde8614306abe32cf40c55b795783f0e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 11:51:43 +0000 Subject: Implement proper locking for headsets --- audio/headset.c | 18 +++++++++--------- audio/headset.h | 9 +++++++-- audio/pcm_bluetooth.c | 24 ++++++++++++++++++------ audio/unix.c | 37 +++++++++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 21 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 25a5ebc6..0f2732e3 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -102,7 +102,7 @@ struct headset { int sp_gain; int mic_gain; - gboolean locked; + headset_lock_t lock; }; static int rfcomm_connect(struct device *device, struct pending_connect *c); @@ -1450,7 +1450,7 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, if (hs->rfcomm && hs->sco) { hs->pending = g_slist_append(hs->pending, c); - g_idle_add((GSourceFunc) finalize_stream_setup, hs); + g_idle_add((GSourceFunc) finalize_stream_setup, dev); return c->id; } @@ -1604,28 +1604,28 @@ gboolean headset_is_active(struct device *dev) return FALSE; } -gboolean headset_lock(struct device *dev) +gboolean headset_lock(struct device *dev, headset_lock_t lock) { struct headset *hs = dev->headset; - if (hs->locked) + if (hs->lock & lock) return FALSE; - hs->locked = TRUE; + hs->lock |= lock; return TRUE; } -gboolean headset_unlock(struct device *dev) +gboolean headset_unlock(struct device *dev, headset_lock_t lock) { struct headset *hs = dev->headset; - if (!hs->locked) + if (!(hs->lock & lock)) return FALSE; - hs->locked = FALSE; + hs->lock &= ~lock; - if (hs->state > HEADSET_STATE_DISCONNECTED) + if (!hs->lock && hs->state > HEADSET_STATE_DISCONNECTED) headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return TRUE; diff --git a/audio/headset.h b/audio/headset.h index 6824e0ce..32bf701b 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -46,6 +46,11 @@ typedef enum { SVC_HANDSFREE } headset_type_t; +typedef enum { + HEADSET_LOCK_READ = 1, + HEADSET_LOCK_WRITE = 1 << 1, +} headset_lock_t; + typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data); struct headset *headset_init(struct device *dev, sdp_record_t *record, @@ -74,7 +79,7 @@ int headset_get_sco_fd(struct device *dev); gboolean headset_is_active(struct device *dev); -gboolean headset_lock(struct device *dev); -gboolean headset_unlock(struct device *dev); +gboolean headset_lock(struct device *dev, headset_lock_t lock); +gboolean headset_unlock(struct device *dev, headset_lock_t lock); gboolean headset_suspend(struct device *dev, void *data); gboolean headset_play(struct device *dev, void *data); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b99f4ccd..5a3b2317 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -893,7 +893,8 @@ static int bluetooth_a2dp_init(struct bluetooth_data *data, return 0; } -static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) +static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, + snd_config_t *conf) { struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; @@ -901,6 +902,15 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) const char *addr, *pref; const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; + switch (stream) { + case SND_PCM_STREAM_PLAYBACK: + cfg->fd_opt = CFG_FD_OPT_WRITE; + break; + case SND_PCM_STREAM_CAPTURE: + cfg->fd_opt = CFG_FD_OPT_READ; + break; + } + snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; @@ -1023,7 +1033,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_config_t *conf) return 0; } -static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) +static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, + snd_config_t *conf) { int ret, total; char buf[IPC_MTU]; @@ -1035,7 +1046,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_config_t *conf) memset(buf, 0, sizeof(buf)); - ret = bluetooth_cfg_init(pkt, conf); + ret = bluetooth_cfg_init(pkt, stream, conf); if (ret < 0) return -ret; @@ -1119,7 +1130,8 @@ done: return 0; } -static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf) +static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, + snd_config_t *conf) { int sk, err; struct sockaddr_un addr = { @@ -1160,7 +1172,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_config_t *conf) if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) return -errno; - return bluetooth_cfg(data, conf); + return bluetooth_cfg(data, stream, conf); } SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) @@ -1177,7 +1189,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) goto error; } - err = bluetooth_init(data, conf); + err = bluetooth_init(data, stream, conf); if (err < 0) goto error; diff --git a/audio/unix.c b/audio/unix.c index 3e8c7469..2cb0d804 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -64,6 +64,10 @@ struct a2dp_data { struct a2dp_sep *sep; }; +struct headset_data { + headset_lock_t lock; +}; + struct unix_client { struct device *dev; struct avdtp_service_capability *media_codec; @@ -71,9 +75,10 @@ struct unix_client { char *interface; union { struct a2dp_data a2dp; - void *data; + struct headset_data hs; } d; int sock; + int fd_opt; unsigned int req_id; unsigned int cb_id; gboolean (*cancel_stream) (struct device *dev, unsigned int id); @@ -245,6 +250,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; struct ipc_data_cfg cfg; + struct headset_data *hs = &client->d.hs; int fd; client->req_id = 0; @@ -255,11 +261,31 @@ static void headset_setup_complete(struct device *dev, void *user_data) return; } - headset_lock(dev); + switch (client->fd_opt) { + case CFG_FD_OPT_READ: + hs->lock = HEADSET_LOCK_READ; + break; + case CFG_FD_OPT_WRITE: + hs->lock = HEADSET_LOCK_WRITE; + break; + case CFG_FD_OPT_READWRITE: + hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; + break; + default: + hs->lock = 0; + break; + } + + if (!headset_lock(dev, hs->lock)) { + error("Unable to lock headset"); + unix_send_cfg(client->sock, NULL, -1); + client->dev = NULL; + return; + } memset(&cfg, 0, sizeof(cfg)); - cfg.fd_opt = CFG_FD_OPT_READWRITE; + cfg.fd_opt = client->fd_opt; cfg.codec = CFG_CODEC_SCO; cfg.mode = CFG_MODE_MONO; cfg.pkt_len = 48; @@ -551,6 +577,8 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le str2ba(pkt->device, &bdaddr); + client->fd_opt = cfg->fd_opt; + if (client->interface) { g_free(client->interface); client->interface = NULL; @@ -618,6 +646,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) struct unix_client *client = data; int len, len_check; struct a2dp_data *a2dp = &client->d.a2dp; + struct headset_data *hs = &client->d.hs; if (cond & G_IO_NVAL) return FALSE; @@ -627,7 +656,7 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) switch (client->type) { case TYPE_HEADSET: if (client->dev) - headset_unlock(client->dev); + headset_unlock(client->dev, hs->lock); break; case TYPE_SOURCE: case TYPE_SINK: -- cgit From d166a230af9dca8be3c27d75e8f1eaf66c49c9d4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 13:01:10 +0000 Subject: Add some extra debug info --- audio/headset.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 0f2732e3..aa066bb7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -594,11 +594,13 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if (!dbus_message_get_args(reply, NULL, + dbus_error_init(&derr); + if (!dbus_message_get_args(reply, &derr, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, DBUS_TYPE_INVALID)) { - error("Unable to get args from GetRecordReply"); + error("Unable to get args from GetRecordReply: %s", derr.message); + dbus_error_free(&derr); goto failed_not_supported; } @@ -717,11 +719,13 @@ static void get_handles_reply(DBusPendingCall *call, void *data) goto failed; } - if (!dbus_message_get_args(reply, NULL, + dbus_error_init(&derr); + if (!dbus_message_get_args(reply, &derr, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { - error("Unable to get args from reply"); + error("Unable to get args from reply: %s", derr.message); + dbus_error_free(&derr); if (c->msg) err_not_supported(device->conn, c->msg); goto failed; -- cgit From edce261bbbcaa3bca4d68289e4f244c1f709c0c9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 14:21:02 +0000 Subject: Fix stream starting if suspend request is pending --- audio/a2dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index fa8d1175..f243a8ae 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1126,8 +1126,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, g_source_remove(sep->suspend_timer); sep->suspend_timer = 0; } + g_idle_add((GSourceFunc) finalize_stream_setup, setup); } - g_idle_add((GSourceFunc) finalize_stream_setup, setup); break; default: error("SEP in bad state for requesting a new stream"); -- cgit From e00800b81712635aade50201cfc3e6eca8b53ae0 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 31 Aug 2007 15:01:12 +0000 Subject: Fix endianess problem. --- audio/a2dp.h | 19 +++++++++++++++++++ audio/avdtp.c | 4 ++-- audio/avdtp.h | 15 +++++++++++++++ audio/pcm_bluetooth.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.h b/audio/a2dp.h index 6a966302..e0d1c2f6 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -47,6 +47,8 @@ #define A2DP_ALLOCATION_SNR (1 << 1) #define A2DP_ALLOCATION_LOUDNESS 1 +#if __BYTE_ORDER == __LITTLE_ENDIAN + struct sbc_codec_cap { struct avdtp_media_codec_capability cap; uint8_t channel_mode:4; @@ -58,6 +60,23 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct sbc_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t frequency:4; + uint8_t channel_mode:4; + uint8_t block_length:4; + uint8_t subbands:2; + uint8_t allocation_method:2; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + struct a2dp_sep; typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep, diff --git a/audio/avdtp.c b/audio/avdtp.c index a50b82b3..2d873bd4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -238,10 +238,10 @@ struct seid_req { struct setconf_req { struct avdtp_header header; - uint8_t int_seid:6; - uint8_t rfa1:2; uint8_t acp_seid:6; uint8_t rfa0:2; + uint8_t int_seid:6; + uint8_t rfa1:2; uint8_t caps[0]; } __attribute__ ((packed)); diff --git a/audio/avdtp.h b/audio/avdtp.h index d8408ac0..e81bf496 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -79,6 +79,8 @@ struct avdtp_service_capability { uint8_t data[0]; } __attribute__ ((packed)); +#if __BYTE_ORDER == __LITTLE_ENDIAN + struct avdtp_media_codec_capability { uint8_t rfa0:4; uint8_t media_type:4; @@ -86,6 +88,19 @@ struct avdtp_media_codec_capability { uint8_t data[0]; } __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_media_codec_capability { + uint8_t media_type:4; + uint8_t rfa0:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, avdtp_state_t old_state, avdtp_state_t new_state, diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 5a3b2317..bc962012 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -65,6 +65,8 @@ #define SCO_RXBUFS 0x04 #endif +#if __BYTE_ORDER == __LITTLE_ENDIAN + struct rtp_header { uint8_t cc:4; uint8_t x:1; @@ -88,6 +90,35 @@ struct rtp_payload { uint8_t is_fragmented:1; } __attribute__ ((packed)); +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + uint8_t v:2; + uint8_t p:1; + uint8_t x:1; + uint8_t cc:4; + + uint8_t m:1; + uint8_t pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t is_fragmented:1; + uint8_t is_first_fragment:1; + uint8_t is_last_fragment:1; + uint8_t rfa0:1; + uint8_t frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ -- cgit From ef302c5314c95183b9751adab86ad6f070ef53ee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 31 Aug 2007 21:00:14 +0000 Subject: Remove transport channel callback before freeing stream --- audio/avdtp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 2d873bd4..c3ed6f96 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -555,6 +555,9 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); + if (stream->io) + g_source_remove(stream->io); + g_slist_foreach(stream->callbacks, (GFunc) g_free, NULL); g_slist_free(stream->callbacks); -- cgit From 36554a48de0e0da15570c6f6aab8f3909fc4ebcb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 2 Sep 2007 16:52:50 +0000 Subject: increase request timeout since the request might not get sent immediatly when the ACL is very congested --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index c3ed6f96..0a17f73e 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -72,7 +72,7 @@ #define AVDTP_MSG_TYPE_ACCEPT 0x02 #define AVDTP_MSG_TYPE_REJECT 0x03 -#define REQ_TIMEOUT 2000 +#define REQ_TIMEOUT 4000 #define DISCONNECT_TIMEOUT 5000 #define STREAM_TIMEOUT 20000 -- cgit From f7ca86c9aa904a915af6720a0add59dd6148cca1 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Sep 2007 13:37:46 +0000 Subject: Fix channel mode bug. --- audio/unix.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 2cb0d804..c39c80c4 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -347,9 +347,23 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, cfg->fd_opt = CFG_FD_OPT_WRITE; sbc_cap = (void *) codec_cap; - cfg->mode = sbc_cap->channel_mode; cfg->sample_size = 2; + switch (sbc_cap->channel_mode) { + case A2DP_CHANNEL_MODE_MONO: + cfg->mode = CFG_MODE_MONO; + break; + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + cfg->mode = CFG_MODE_DUAL_CHANNEL; + break; + case A2DP_CHANNEL_MODE_STEREO: + cfg->mode = CFG_MODE_STEREO; + break; + case A2DP_CHANNEL_MODE_JOINT_STEREO: + cfg->mode = CFG_MODE_JOINT_STEREO; + break; + } + switch (sbc_cap->frequency) { case A2DP_SAMPLING_FREQ_16000: cfg->rate = 16000; -- cgit From e1ca1c0fbb4694b4b2c8e7532b05dbfcd4144687 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 3 Sep 2007 19:12:40 +0000 Subject: stub in the state change ipc for pausing a stream (but don't enable it yet) --- audio/pcm_bluetooth.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ audio/unix.c | 47 +++++++++++++++++++------------------- 2 files changed, 85 insertions(+), 24 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index bc962012..7cf802b8 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -215,6 +215,62 @@ iter_sleep: } } +static int bluetooth_state_init(struct ipc_packet *pkt, int newstate) +{ + struct ipc_data_state *state = (void *) pkt->data; + + pkt->length = sizeof(*state); + pkt->type = PKT_TYPE_STATE_REQ; + pkt->error = PKT_ERROR_NONE; + state->state = newstate; + + return 0; +} + +static int bluetooth_state(struct bluetooth_data *data, int newstate) +{ + char buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_state *state = (void *) pkt->data; + int ret, total; + + memset(buf, 0, sizeof(buf)); + + ret = bluetooth_state_init(pkt, newstate); + if (ret < 0) + return -ret; + + ret = send(data->sock, pkt, sizeof(*pkt) + pkt->length, 0); + if (ret < 0) + return -errno; + else if (ret == 0) + return -EIO; + + DBG("OK - %d bytes sent. Waiting for response...", ret); + + memset(buf, 0, sizeof(buf)); + + ret = recv(data->sock, buf, sizeof(*pkt) + sizeof(*state), 0); + if (ret < 0) + return -errno; + else if (ret == 0) + return -EIO; + + total = ret; + + if (pkt->type != PKT_TYPE_STATE_RSP) { + SNDERR("Unexpected packet type %d received", pkt->type); + return -EINVAL; + } + + if (pkt->error != PKT_ERROR_NONE) { + SNDERR("Error %d while configuring device", pkt->error); + return -pkt->error; + } + + return 0; +} + static int bluetooth_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -222,6 +278,9 @@ static int bluetooth_playback_start(snd_pcm_ioplug_t *io) DBG("%p", io); +#if 0 + bluetooth_state(data, STATE_STREAMING); +#endif data->stopped = 0; if (data->hw_thread) @@ -238,6 +297,9 @@ static int bluetooth_playback_stop(snd_pcm_ioplug_t *io) DBG("%p", io); +#if 0 + bluetooth_state(data, STATE_CONNECTED); +#endif data->stopped = 1; return 0; diff --git a/audio/unix.c b/audio/unix.c index c39c80c4..accf6efb 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -637,6 +637,27 @@ static void ctl_event(struct unix_client *client, { } +static int reply_state(int sock, struct ipc_packet *pkt) +{ + struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; + int len; + + info("status=%u", state->state); + + pkt->type = PKT_TYPE_STATE_RSP; + pkt->length = sizeof(struct ipc_data_state); + pkt->error = PKT_ERROR_NONE; + + len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); + len = send(sock, pkt, len, 0); + if (len < 0) + error("Error %s(%d)", strerror(errno), errno); + + debug("%d bytes sent", len); + + return 0; +} + static void state_event(struct unix_client *client, struct ipc_packet *pkt, int len) { @@ -648,9 +669,9 @@ static void state_event(struct unix_client *client, device_set_state(dev, state->state); else state->state = device_get_state(dev); - - unix_send_state(client->sock, pkt); #endif + + reply_state(client->sock, pkt); } static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) @@ -812,25 +833,3 @@ void unix_exit(void) unix_sock = -1; } -#if 0 -static int unix_send_state(int sock, struct ipc_packet *pkt) -{ - struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - int len; - - info("status=%u", state->state); - - pkt->type = PKT_TYPE_STATE_RSP; - pkt->length = sizeof(struct ipc_data_state); - pkt->error = PKT_ERROR_NONE; - - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - - debug("%d bytes sent", len); - - return 0; -} -#endif -- cgit From d5566fac231129b8c8e35db1cada19ed83cb3a18 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 3 Sep 2007 19:18:35 +0000 Subject: removed unused variable total from bluetooth_state --- audio/pcm_bluetooth.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 7cf802b8..2c36bed9 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -232,7 +232,7 @@ static int bluetooth_state(struct bluetooth_data *data, int newstate) char buf[IPC_MTU]; struct ipc_packet *pkt = (void *) buf; struct ipc_data_state *state = (void *) pkt->data; - int ret, total; + int ret; memset(buf, 0, sizeof(buf)); @@ -256,8 +256,6 @@ static int bluetooth_state(struct bluetooth_data *data, int newstate) else if (ret == 0) return -EIO; - total = ret; - if (pkt->type != PKT_TYPE_STATE_RSP) { SNDERR("Unexpected packet type %d received", pkt->type); return -EINVAL; -- cgit From c4be6b437310018f154f7a01e6a3ddc2c9d1e175 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Sep 2007 21:21:20 +0000 Subject: Limit range of bitpool announced while in ACP side. --- audio/a2dp.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index f243a8ae..5dd85a5e 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -41,6 +41,9 @@ #include "sink.h" #include "a2dp.h" +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2 + /* The duration that streams without users are allowed to stay in * STREAMING state. */ #define SUSPEND_TIMEOUT 5000 @@ -274,7 +277,7 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, else if (supported->allocation_method & A2DP_ALLOCATION_SNR) cap->allocation_method = A2DP_ALLOCATION_SNR; - min_bitpool = MAX(2, supported->min_bitpool); + min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), supported->max_bitpool); @@ -371,6 +374,9 @@ static gboolean setconf_ind(struct avdtp *session, { struct a2dp_sep *a2dp_sep = user_data; struct device *dev; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap; + struct sbc_codec_cap *sbc_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Set_Configuration_Ind"); @@ -384,6 +390,24 @@ static gboolean setconf_ind(struct avdtp *session, return FALSE; } + /* Check bipool range */ + for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { + cap = caps->data; + if (cap->category == AVDTP_MEDIA_CODEC) { + codec_cap = (void *) cap->data; + if (codec_cap->media_codec_type == A2DP_CODEC_SBC) { + sbc_cap = (void *) codec_cap; + if (sbc_cap->min_bitpool < MIN_BITPOOL || + sbc_cap->max_bitpool > MAX_BITPOOL) { + *err = AVDTP_UNSUPPORTED_CONFIGURATION; + *category = AVDTP_MEDIA_CODEC; + return FALSE; + } + } + break; + } + } + avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); a2dp_sep->stream = stream; @@ -437,8 +461,8 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, sbc_cap.allocation_method = ( A2DP_ALLOCATION_LOUDNESS | A2DP_ALLOCATION_SNR ); - sbc_cap.min_bitpool = 2; - sbc_cap.max_bitpool = 250; + sbc_cap.min_bitpool = MIN_BITPOOL; + sbc_cap.max_bitpool = MAX_BITPOOL; media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, sizeof(sbc_cap)); -- cgit From c4e21c814832ea94f64821a2360a28074d03be1a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 5 Sep 2007 14:27:18 +0000 Subject: Cleanup fd passing a little --- audio/pcm_bluetooth.c | 45 ++++++++++++++++++--------------------------- audio/unix.c | 26 ++++++++++---------------- 2 files changed, 28 insertions(+), 43 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2c36bed9..da5927d0 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -907,22 +907,17 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_recvmsg_fd(struct bluetooth_data *data) { - char cmsg_b[CMSG_SPACE(sizeof(int))]; - struct ipc_packet pkt; + char cmsg_b[CMSG_SPACE(sizeof(int))], m; int err, ret; - struct iovec iov = { - .iov_base = &pkt, - .iov_len = sizeof(pkt) - }; - struct msghdr msgh = { - .msg_name = 0, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg_b, - .msg_controllen = CMSG_LEN(sizeof(int)), - .msg_flags = 0 - }; + struct iovec iov = { &m, sizeof(m) }; + struct msghdr msgh; + struct cmsghdr *cmsg; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(sizeof(int)); ret = recvmsg(data->sock, &msgh, 0); if (ret < 0) { @@ -931,20 +926,16 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) return -err; } - if (pkt.type == PKT_TYPE_CFG_RSP) { - struct cmsghdr *cmsg; - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh,cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - data->stream_fd = (*(int *) CMSG_DATA(cmsg)); - DBG("stream_fd=%d", data->stream_fd); - return 0; - } + data->stream_fd = (*(int *) CMSG_DATA(cmsg)); + DBG("stream_fd=%d", data->stream_fd); + return 0; } - } else - SNDERR("Unexpected packet type %d received", pkt.type); + } return -EINVAL; } diff --git a/audio/unix.c b/audio/unix.c index accf6efb..cf225ae5 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -122,24 +122,18 @@ static void client_free(struct unix_client *client) and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the handle of the file descriptor to be passed.*/ -static int unix_sendmsg_fd(int sock, int fd, struct ipc_packet *pkt) +static int unix_sendmsg_fd(int sock, int fd) { - char cmsg_b[CMSG_SPACE(sizeof(int))]; + char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm'; struct cmsghdr *cmsg; - struct iovec iov = { - .iov_base = pkt, - .iov_len = sizeof(struct ipc_packet) - }; + struct iovec iov = { &m, sizeof(m) }; + struct msghdr msgh; - struct msghdr msgh = { - .msg_name = 0, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &cmsg_b, - .msg_controllen = CMSG_LEN(sizeof(int)), - .msg_flags = 0 - }; + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(sizeof(int)); cmsg = CMSG_FIRSTHDR(&msgh); cmsg->cmsg_level = SOL_SOCKET; @@ -237,7 +231,7 @@ static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) debug("%d bytes sent", len); if (fd != -1) { - len = unix_sendmsg_fd(sock, fd, pkt); + len = unix_sendmsg_fd(sock, fd); if (len < 0) error("Error %s(%d)", strerror(errno), errno); debug("%d bytes sent", len); -- cgit From e4b3ec84ce3680ca33ae88b76e1ce138756c0427 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 5 Sep 2007 21:25:25 +0000 Subject: Use poll instead of usleep to wait on worker thread. --- audio/pcm_bluetooth.c | 81 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 30 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index da5927d0..9e5058c0 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -43,7 +43,7 @@ #define UINT_SECS_MAX (UINT_MAX / 1000000 - 1) -#define MIN_PERIOD_TIME 1000 +#define MIN_PERIOD_TIME 1 #define BUFFER_SIZE 2048 @@ -135,8 +135,8 @@ struct bluetooth_data { snd_pcm_ioplug_t io; volatile snd_pcm_sframes_t hw_ptr; struct ipc_data_cfg cfg; /* Bluetooth device config */ - int stream_fd; /* Audio stream filedescriptor */ - int sock; /* Daemon unix socket */ + struct pollfd stream; /* Audio stream filedescriptor */ + struct pollfd server; /* Audio daemon filedescriptor */ uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ @@ -166,6 +166,10 @@ static void *playback_hw_thread(void *param) unsigned int prev_periods; double period_time; struct timeval start; + struct pollfd fds[2]; + + fds[0] = data->server; + fds[1] = data->stream; prev_periods = 0; period_time = 1000000.0 * data->io.period_size / data->io.rate; @@ -175,6 +179,7 @@ static void *playback_hw_thread(void *param) while (1) { unsigned int dtime, periods; struct timeval cur, delta; + int ret; if (data->stopped) goto iter_sleep; @@ -208,11 +213,26 @@ static void *playback_hw_thread(void *param) } iter_sleep: - usleep(MIN_PERIOD_TIME); + ret = poll(fds, 2, MIN_PERIOD_TIME); + if (ret < 0) { + SNDERR("poll error: %s (%d)", strerror(errno), errno); + if (errno != EINTR) + break; + } else if (ret > 0) { + ret = (fds[0].revents) ? 0 : 1; + SNDERR("poll fd %d revents %d", ret, fds[ret].revents); + if (fds[ret].revents & POLLERR || + fds[ret].revents & POLLHUP || + fds[ret].revents & POLLNVAL) + break; + } /* Offer opportunity to be canceled by main thread */ pthread_testcancel(); } + + data->hw_thread = 0; + pthread_exit(NULL); } static int bluetooth_state_init(struct ipc_packet *pkt, int newstate) @@ -240,7 +260,7 @@ static int bluetooth_state(struct bluetooth_data *data, int newstate) if (ret < 0) return -ret; - ret = send(data->sock, pkt, sizeof(*pkt) + pkt->length, 0); + ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); if (ret < 0) return -errno; else if (ret == 0) @@ -250,7 +270,7 @@ static int bluetooth_state(struct bluetooth_data *data, int newstate) memset(buf, 0, sizeof(buf)); - ret = recv(data->sock, buf, sizeof(*pkt) + sizeof(*state), 0); + ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*state), 0); if (ret < 0) return -errno; else if (ret == 0) @@ -314,11 +334,11 @@ static void bluetooth_exit(struct bluetooth_data *data) { struct bluetooth_a2dp *a2dp = &data->a2dp; - if (data->sock >= 0) - close(data->sock); + if (data->server.fd >= 0) + close(data->server.fd); - if (data->stream_fd >= 0) - close(data->stream_fd); + if (data->stream.fd >= 0) + close(data->stream.fd); if (data->hw_thread) { pthread_cancel(data->hw_thread); @@ -376,19 +396,19 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, uint32_t period_count = io->buffer_size / io->period_size; int opt_name, err; - DBG("fd=%d period_count=%d", data->stream_fd, period_count); + DBG("fd=%d period_count=%d", data->stream.fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; - if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) return 0; opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDBUF : SO_RCVBUF; - if (setsockopt(data->stream_fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) return 0; @@ -408,12 +428,12 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, int opt_name, err; struct timeval t = { 0, period_count }; - DBG("fd=%d period_count=%d", data->stream_fd, period_count); + DBG("fd=%d period_count=%d", data->stream.fd, period_count); opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDTIMEO : SO_RCVTIMEO; - if (setsockopt(data->stream_fd, SOL_SOCKET, opt_name, &t, + if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, sizeof(t)) == 0) return 0; @@ -434,7 +454,7 @@ static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, if (space < 1) return 0; - pfd[0].fd = data->stream_fd; + pfd[0].fd = data->stream.fd; pfd[0].events = POLLIN; pfd[0].revents = 0; @@ -512,7 +532,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, frame_size = areas->step / 8; - nrecv = recv(data->stream_fd, data->buffer, cfg.pkt_len, + nrecv = recv(data->stream.fd, data->buffer, cfg.pkt_len, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); if (nrecv < 0) { @@ -592,7 +612,7 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, goto done; } - rsend = send(data->stream_fd, data->buffer, cfg.pkt_len, + rsend = send(data->stream.fd, data->buffer, cfg.pkt_len, io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ @@ -636,7 +656,7 @@ static int avdtp_write(struct bluetooth_data *data) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - ret = send(data->stream_fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); + ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); if (ret == -1) ret = -errno; @@ -919,7 +939,7 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) msgh.msg_control = &cmsg_b; msgh.msg_controllen = CMSG_LEN(sizeof(int)); - ret = recvmsg(data->sock, &msgh, 0); + ret = recvmsg(data->server.fd, &msgh, 0); if (ret < 0) { err = errno; SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); @@ -931,8 +951,8 @@ static int bluetooth_recvmsg_fd(struct bluetooth_data *data) cmsg = CMSG_NXTHDR(&msgh, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - data->stream_fd = (*(int *) CMSG_DATA(cmsg)); - DBG("stream_fd=%d", data->stream_fd); + data->stream.fd = (*(int *) CMSG_DATA(cmsg)); + DBG("stream_fd=%d", data->stream.fd); return 0; } } @@ -1132,7 +1152,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, if (ret < 0) return -ret; - ret = send(data->sock, pkt, sizeof(*pkt) + pkt->length, 0); + ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); if (ret < 0) return -errno; else if (ret == 0) @@ -1142,7 +1162,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, memset(buf, 0, sizeof(buf)); - ret = recv(data->sock, buf, sizeof(*pkt) + sizeof(*cfg), 0); + ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*cfg), 0); if (ret < 0) return -errno; else if (ret == 0) @@ -1163,7 +1183,7 @@ static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, if (cfg->codec != CFG_CODEC_SBC) goto done; - ret = recv(data->sock, sbc, sizeof(*sbc), 0); + ret = recv(data->server.fd, sbc, sizeof(*sbc), 0); if (ret < 0) return -errno; else if (ret == 0) @@ -1184,7 +1204,7 @@ done: DBG("Device configuration:"); DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - data->stream_fd, data->cfg.fd_opt, data->cfg.pkt_len, + data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len, data->cfg.sample_size, data->cfg.rate); if (data->cfg.codec == CFG_CODEC_SBC) { @@ -1193,7 +1213,7 @@ done: return ret; } - if (data->stream_fd == -1) { + if (data->stream.fd == -1) { SNDERR("Error while configuring device: could not acquire audio socket"); return -EINVAL; } @@ -1204,7 +1224,7 @@ done: /* It is possible there is some outstanding data in the pipe - we have to empty it */ - while (recv(data->stream_fd, data->buffer, data->cfg.pkt_len, + while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len, MSG_DONTWAIT) > 0); memset(data->buffer, 0, sizeof(data->buffer)); @@ -1225,7 +1245,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, memset(data, 0, sizeof(struct bluetooth_data)); - data->sock = -1; + data->server.fd = -1; sk = socket(PF_LOCAL, SOCK_STREAM, 0); if (sk < 0) { @@ -1242,7 +1262,8 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, return -err; } - data->sock = sk; + data->server.fd = sk; + data->server.events = POLLIN; data->pipefd[0] = -1; data->pipefd[1] = -1; -- cgit From db66c9850fba4892b0c676e2d4d94171dcbae344 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 6 Sep 2007 03:20:38 +0000 Subject: Fix poll revents check --- audio/pcm_bluetooth.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 9e5058c0..6338bc25 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -221,9 +221,7 @@ iter_sleep: } else if (ret > 0) { ret = (fds[0].revents) ? 0 : 1; SNDERR("poll fd %d revents %d", ret, fds[ret].revents); - if (fds[ret].revents & POLLERR || - fds[ret].revents & POLLHUP || - fds[ret].revents & POLLNVAL) + if (fds[ret].revents & (POLLERR | POLLHUP | POLLNVAL)) break; } -- cgit From dc6a67eda727b063558e9ae95b05ed16110a3108 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 6 Sep 2007 07:33:06 +0000 Subject: Check for valid stream fd after trying to receive it (and not before) --- audio/pcm_bluetooth.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6338bc25..dd61089f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -1211,15 +1211,15 @@ done: return ret; } + ret = bluetooth_recvmsg_fd(data); + if (ret < 0) + return ret; + if (data->stream.fd == -1) { SNDERR("Error while configuring device: could not acquire audio socket"); return -EINVAL; } - ret = bluetooth_recvmsg_fd(data); - if (ret < 0) - return ret; - /* It is possible there is some outstanding data in the pipe - we have to empty it */ while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len, @@ -1244,6 +1244,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, memset(data, 0, sizeof(struct bluetooth_data)); data->server.fd = -1; + data->stream.fd = -1; sk = socket(PF_LOCAL, SOCK_STREAM, 0); if (sk < 0) { -- cgit From cf258cad55202b81faa902b7559dad2ec07d8ad1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 16 Sep 2007 11:11:00 +0000 Subject: Fix compiler warning --- audio/pcm_bluetooth.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index dd61089f..fb3fce13 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -233,6 +233,7 @@ iter_sleep: pthread_exit(NULL); } +#if 0 static int bluetooth_state_init(struct ipc_packet *pkt, int newstate) { struct ipc_data_state *state = (void *) pkt->data; @@ -286,6 +287,7 @@ static int bluetooth_state(struct bluetooth_data *data, int newstate) return 0; } +#endif static int bluetooth_playback_start(snd_pcm_ioplug_t *io) { -- cgit From 5dc5142303d645d4abfbcf197f352ae7ca4a3f9a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 17 Sep 2007 14:23:26 +0000 Subject: Clear pending request stream reference if stream goes away (e.g. media transpor connection dies) before the request is replied to. --- audio/avdtp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 0a17f73e..2f12544a 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -685,6 +685,8 @@ static void avdtp_sep_set_state(struct avdtp *session, session->streams = g_slist_remove(session->streams, stream); if (session->pending_open == stream) handle_transport_connect(session, -1, 0); + if (session->req && session->req->stream == stream) + session->req->stream = NULL; stream_free(stream); if (session->ref == 1 && !session->streams) set_disconnect_timer(session); -- cgit From 7a3857fd8a6fb641134f356b72c50f860c852ef4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 21 Sep 2007 13:11:17 +0000 Subject: Fix bogus error checking. --- audio/avdtp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 2f12544a..7907763a 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1510,11 +1510,6 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, sk = g_io_channel_unix_get_fd(chan); - if (cond & (G_IO_ERR | G_IO_HUP)) { - err = EIO; - goto failed; - } - len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { err = errno; @@ -1528,6 +1523,11 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, goto failed; } + if (cond & G_IO_HUP) { + err = EIO; + goto failed; + } + ba2str(&session->dst, address); debug("AVDTP: connected %s channel to %s", session->pending_open ? "transport" : "signaling", -- cgit From d3074a97b9803de16a65e6d377060b72f026dfd7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 27 Sep 2007 07:53:29 +0000 Subject: Add missing NULL check --- audio/manager.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 30b78632..43635578 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -347,7 +347,8 @@ done: data->cb(data->device, data->cb_data); } else { remove_device(data->device); - data->cb(NULL, data->cb_data); + if (data->cb) + data->cb(NULL, data->cb_data); } if (data->msg) dbus_message_unref(data->msg); -- cgit From 2fb6bb97176c9e922c14584da550f3edd49f0986 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 28 Sep 2007 07:34:37 +0000 Subject: Check for zero length files --- audio/gstbluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 7e02e202..ce065820 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -38,7 +38,7 @@ static void sbc_typefind(GstTypeFind *tf, gpointer ignore) { guint8 *data = gst_type_find_peek(tf, 0, 1); - if (*data != 0x9c) /* SBC syncword */ + if (data == NULL || *data != 0x9c) /* SBC syncword */ return; gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, SBC_CAPS); -- cgit From 847dad179fe16ec395373e4b55d46dcd400c623a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 28 Sep 2007 13:46:55 +0000 Subject: Expose stream setup errors higher up in the call stack --- audio/a2dp.c | 102 +++++++++++++++++++++++++++++++++++++--------------------- audio/a2dp.h | 3 +- audio/avdtp.c | 22 ++++--------- audio/avdtp.h | 17 ++++++++-- audio/sink.c | 4 +-- 5 files changed, 91 insertions(+), 57 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 5dd85a5e..cb43dce2 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -26,6 +26,7 @@ #endif #include +#include #include #include @@ -112,19 +113,29 @@ static struct device *a2dp_get_dev(struct avdtp *session) return manager_device_connected(&addr, A2DP_SOURCE_UUID); } -static void setup_callback(struct a2dp_stream_cb *cb, - struct a2dp_stream_setup *s) +static gboolean finalize_stream_setup(struct a2dp_stream_setup *s, struct avdtp_error *err) { - cb->cb(s->session, s->sep, s->stream, cb->user_data); -} + GSList *l; + + for (l = s->cb; l != NULL; l = l->next) { + struct a2dp_stream_cb *cb = l->data; + + cb->cb(s->session, s->sep, s->stream, cb->user_data, err); + } -static gboolean finalize_stream_setup(struct a2dp_stream_setup *s) -{ - g_slist_foreach(s->cb, (GFunc) setup_callback, s); stream_setup_free(s); return FALSE; } +static gboolean finalize_stream_setup_errno(struct a2dp_stream_setup *s, int err) +{ + struct avdtp_error avdtp_err; + + avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + + return finalize_stream_setup(s, err ? &avdtp_err : NULL); +} + static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session) { GSList *l; @@ -324,22 +335,23 @@ static gboolean a2dp_select_capabilities(struct avdtp *session, return TRUE; } -static void discovery_complete(struct avdtp *session, GSList *seps, int err, +static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, void *user_data) { struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; struct a2dp_stream_setup *setup; GSList *caps = NULL; + int posix_err; setup = find_setup_by_session(session); if (!setup) return; - if (err < 0 || setup->canceled) { + if (err || setup->canceled) { setup->stream = NULL; - finalize_stream_setup(setup); + finalize_stream_setup(setup, err); return; } @@ -348,21 +360,21 @@ static void discovery_complete(struct avdtp *session, GSList *seps, int err, if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, &lsep, &rsep) < 0) { error("No matching ACP and INT SEPs found"); - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, -EINVAL); return; } if (!a2dp_select_capabilities(session, rsep, &caps)) { error("Unable to select remote SEP capabilities"); - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, -EINVAL); return; } - err = avdtp_set_configuration(session, rsep, lsep, caps, + posix_err = avdtp_set_configuration(session, rsep, lsep, caps, &setup->stream); - if (err < 0) { - error("avdtp_set_configuration: %s", strerror(-err)); - finalize_stream_setup(setup); + if (posix_err < 0) { + error("avdtp_set_configuration: %s", strerror(-posix_err)); + finalize_stream_setup_errno(setup, posix_err); } } @@ -490,7 +502,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { if (setup) - finalize_stream_setup(setup); + finalize_stream_setup(setup, err); return; } @@ -510,7 +522,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, error("Error on avdtp_open %s (%d)", strerror(-ret), -ret); setup->stream = NULL; - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, ret); } } @@ -557,6 +569,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_stream_setup *setup; + int posix_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Cfm"); @@ -576,19 +589,22 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - goto finalize; + finalize_stream_setup(setup, err); + return; } if (setup->start) { - if (avdtp_start(session, stream) == 0) + posix_err = avdtp_start(session, stream); + if (posix_err == 0) return; error("avdtp_start failed"); - setup->stream = NULL; + setup->stream = NULL; } + else + posix_err = 0; -finalize: - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, -posix_err); } static gboolean suspend_timeout(struct a2dp_sep *sep) @@ -646,10 +662,12 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; } - if (err) + if (err) { setup->stream = NULL; - - finalize_stream_setup(setup); + finalize_stream_setup(setup, err); + } + else + finalize_stream_setup_errno(setup, 0); } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -671,6 +689,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_stream_setup *setup; + int posix_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); @@ -684,13 +703,14 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (err) { - finalize_stream_setup(setup); + finalize_stream_setup(setup, err); return; } if (setup->start) { - if (avdtp_start(session, stream) < 0) - finalize_stream_setup(setup); + posix_err = avdtp_start(session, stream); + if (posix_err < 0) + finalize_stream_setup_errno(setup, posix_err); } } @@ -714,6 +734,7 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_stream_setup *setup; + int posix_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); @@ -731,19 +752,22 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - goto finalize; + finalize_stream_setup(setup, err); + return; } if (setup->start) { - if (avdtp_discover(session, discovery_complete, setup) == 0) + posix_err = avdtp_discover(session, discovery_complete, setup); + if (posix_err == 0) return; error("avdtp_discover failed"); setup->stream = NULL; } + else + posix_err = 0; -finalize: - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, -posix_err); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -792,6 +816,7 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_stream_setup *setup; + int posix_err; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); @@ -811,19 +836,22 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - goto finalize; + finalize_stream_setup(setup, err); + return; } if (setup->start) { - if (avdtp_start(session, stream) == 0) + posix_err = avdtp_start(session, stream); + if (posix_err == 0) return; error("avdtp_start failed"); setup->stream = NULL; } + else + posix_err = 0; -finalize: - finalize_stream_setup(setup); + finalize_stream_setup_errno(setup, -posix_err); } static struct avdtp_sep_cfm cfm = { diff --git a/audio/a2dp.h b/audio/a2dp.h index e0d1c2f6..dffdf259 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -81,7 +81,8 @@ struct a2dp_sep; typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, - void *user_data); + void *user_data, + struct avdtp_error *err); int a2dp_init(DBusConnection *conn, int sources, int sinks); void a2dp_exit(void); diff --git a/audio/avdtp.c b/audio/avdtp.c index 7907763a..ee1bd06d 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -76,11 +76,6 @@ #define DISCONNECT_TIMEOUT 5000 #define STREAM_TIMEOUT 20000 -typedef enum { - AVDTP_ERROR_ERRNO, - AVDTP_ERROR_ERROR_CODE -} avdtp_error_type_t; - typedef enum { AVDTP_SESSION_STATE_DISCONNECTED, AVDTP_SESSION_STATE_CONNECTING, @@ -362,14 +357,6 @@ struct avdtp { DBusPendingCall *pending_auth; }; -struct avdtp_error { - avdtp_error_type_t type; - union { - uint8_t error_code; - int posix_errno; - } err; -}; - static uint8_t free_seid = 1; static GSList *local_seps = NULL; @@ -499,7 +486,7 @@ static void set_disconnect_timer(struct avdtp *session) disconnect_timeout, session); } -static void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) +void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) { err->type = type; switch (type) { @@ -698,10 +685,15 @@ static void avdtp_sep_set_state(struct avdtp *session, static void finalize_discovery(struct avdtp *session, int err) { + struct avdtp_error avdtp_err; + + avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + if (!session->discov_cb) return; - session->discov_cb(session, session->seps, err, + session->discov_cb(session, session->seps, + err ? &avdtp_err : NULL, session->user_data); session->discov_cb = NULL; diff --git a/audio/avdtp.h b/audio/avdtp.h index e81bf496..6af47e57 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -21,11 +21,22 @@ * */ +typedef enum { + AVDTP_ERROR_ERRNO, + AVDTP_ERROR_ERROR_CODE +} avdtp_error_type_t; + struct avdtp; struct avdtp_stream; struct avdtp_local_sep; struct avdtp_remote_sep; -struct avdtp_error; +struct avdtp_error { + avdtp_error_type_t type; + union { + uint8_t error_code; + int posix_errno; + } err; +}; /* SEP capability categories */ #define AVDTP_MEDIA_TRANSPORT 0x01 @@ -177,7 +188,7 @@ struct avdtp_sep_ind { }; typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, - int err, void *user_data); + struct avdtp_error *err, void *user_data); struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst); @@ -240,7 +251,9 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep); avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); +void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); const char *avdtp_strerror(struct avdtp_error *err); +int avdtp_error_code(struct avdtp_error *err); void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); diff --git a/audio/sink.c b/audio/sink.c index ea95bcf7..e249af18 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -135,7 +135,7 @@ static void stream_state_changed(struct avdtp_stream *stream, static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, - void *user_data) + void *user_data, struct avdtp_error *err) { struct sink *sink = user_data; struct pending_request *pending; @@ -153,7 +153,7 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; - debug("Stream setup failed"); + debug("Stream setup failed : %s", avdtp_strerror(err)); } pending_request_free(pending); -- cgit From b0a7532f6111103e4e2e21eb45d30a34c49f1f77 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 2 Oct 2007 15:15:58 +0000 Subject: Fix audio service SDP failure handling --- audio/manager.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 43635578..18997556 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -88,6 +88,8 @@ struct audio_sdp_data { audio_sdp_state_t state; + gboolean remove_on_fail; + create_dev_cb_t cb; void *cb_data; }; @@ -295,8 +297,12 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) device_finish_sdp_transaction(data->device); - if (!success) + if (!success) { + error("finish_sdp: SDP failed"); + if (data->msg) + err_failed(connection, data->msg, "Failed"); goto done; + } if (!data->msg) goto update; @@ -309,6 +315,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (dbus_error_is_set(&derr)) { error("Unable to get message args"); success = FALSE; + err_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto done; } @@ -346,7 +353,8 @@ done: if (data->cb) data->cb(data->device, data->cb_data); } else { - remove_device(data->device); + if (data->remove_on_fail) + remove_device(data->device); if (data->cb) data->cb(NULL, data->cb_data); } @@ -585,6 +593,7 @@ failed: static DBusHandlerResult resolve_services(DBusMessage *msg, struct device *device, + gboolean remove_on_fail, create_dev_cb_t cb, void *user_data) { @@ -594,6 +603,7 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, if (msg) sdp_data->msg = dbus_message_ref(msg); sdp_data->device = device; + sdp_data->remove_on_fail = remove_on_fail; sdp_data->cb = cb; sdp_data->cb_data = user_data; @@ -648,7 +658,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - resolve_services(NULL, device, NULL, NULL); + resolve_services(NULL, device, FALSE, NULL, NULL); } if (headset) @@ -688,7 +698,7 @@ gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, if (!dev) return FALSE; - resolve_services(NULL, dev, cb, user_data); + resolve_services(NULL, dev, TRUE, cb, user_data); return TRUE; } @@ -719,7 +729,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, device = manager_find_device(&bda, NULL, FALSE); if (!device) { device = create_device(&bda); - return resolve_services(msg, device, NULL, NULL); + return resolve_services(msg, device, TRUE, NULL, NULL); } path = device->path; -- cgit From b2df0e92432477022e06bbc6df65a6f394818ee2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 2 Oct 2007 15:48:22 +0000 Subject: Further fixes to audio service SDP error handling --- audio/manager.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 18997556..e1fb81ce 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -88,8 +88,6 @@ struct audio_sdp_data { audio_sdp_state_t state; - gboolean remove_on_fail; - create_dev_cb_t cb; void *cb_data; }; @@ -124,6 +122,11 @@ static struct device *create_device(bdaddr_t *bda) return device_register(connection, path, bda); } +static void destroy_device(struct device *device) +{ + dbus_connection_destroy_object_path(connection, device->path); +} + static void remove_device(struct device *device) { if (device == default_dev) { @@ -138,7 +141,7 @@ static void remove_device(struct device *device) devices = g_slist_remove(devices, device); - dbus_connection_destroy_object_path(connection, device->path); + destroy_device(device); } static gboolean add_device(struct device *device, gboolean send_signals) @@ -297,12 +300,8 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) device_finish_sdp_transaction(data->device); - if (!success) { - error("finish_sdp: SDP failed"); - if (data->msg) - err_failed(connection, data->msg, "Failed"); + if (!success) goto done; - } if (!data->msg) goto update; @@ -353,10 +352,10 @@ done: if (data->cb) data->cb(data->device, data->cb_data); } else { - if (data->remove_on_fail) - remove_device(data->device); if (data->cb) data->cb(NULL, data->cb_data); + if (!g_slist_find(devices, data->device)) + destroy_device(data->device); } if (data->msg) dbus_message_unref(data->msg); @@ -593,7 +592,6 @@ failed: static DBusHandlerResult resolve_services(DBusMessage *msg, struct device *device, - gboolean remove_on_fail, create_dev_cb_t cb, void *user_data) { @@ -603,7 +601,6 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, if (msg) sdp_data->msg = dbus_message_ref(msg); sdp_data->device = device; - sdp_data->remove_on_fail = remove_on_fail; sdp_data->cb = cb; sdp_data->cb_data = user_data; @@ -622,7 +619,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) if (!device) return NULL; if (!add_device(device, TRUE)) { - remove_device(device); + destroy_device(device); return NULL; } created = TRUE; @@ -658,7 +655,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) "DeviceCreated", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - resolve_services(NULL, device, FALSE, NULL, NULL); + resolve_services(NULL, device, NULL, NULL); } if (headset) @@ -698,7 +695,7 @@ gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, if (!dev) return FALSE; - resolve_services(NULL, dev, TRUE, cb, user_data); + resolve_services(NULL, dev, cb, user_data); return TRUE; } @@ -729,7 +726,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, device = manager_find_device(&bda, NULL, FALSE); if (!device) { device = create_device(&bda); - return resolve_services(msg, device, TRUE, NULL, NULL); + return resolve_services(msg, device, NULL, NULL); } path = device->path; -- cgit From 91e4a8e4a2c430a987d735a2241b0e9bd5e2d978 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Oct 2007 13:18:18 +0000 Subject: Error handling fixes --- audio/headset.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index aa066bb7..5759ebe0 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -346,10 +346,9 @@ static GIOError headset_send(struct headset *hs, const char *str) static void pending_connect_ok(struct pending_connect *c, struct device *dev) { struct headset *hs = dev->headset; - DBusMessage *reply; if (c->msg) { - reply = dbus_message_new_method_return(c->msg); + DBusMessage *reply = dbus_message_new_method_return(c->msg); if (reply) send_message_and_unref(dev->conn, reply); } @@ -389,7 +388,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, { struct headset *hs; struct pending_connect *c; - int ret, sk, err; + int ret, sk; socklen_t len; if (cond & G_IO_NVAL) @@ -402,13 +401,14 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); + c->err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err), + c->err); goto failed; } if (ret != 0) { - err = ret; + c->err = ret; error("connect(): %s (%d)", strerror(ret), ret); goto failed; } @@ -511,7 +511,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct headset *hs; struct pending_connect *c; char hs_address[18]; - int sk, ret, err = 0; + int sk, ret; socklen_t len; if (cond & G_IO_NVAL) @@ -524,13 +524,13 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); + c->err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err), c->err); goto failed; } if (ret != 0) { - err = ret; + c->err = ret; error("connect(): %s (%d)", strerror(ret), ret); goto failed; } @@ -547,8 +547,10 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, (GIOFunc) rfcomm_io_cb, device); if (c->cb) { - if (sco_connect(device, c) < 0) + if (sco_connect(device, c) < 0) { + c->err = EIO; goto failed; + } return FALSE; } @@ -654,11 +656,10 @@ static void get_record_reply(DBusPendingCall *call, void *data) hs->rfcomm_ch = ch; - if ((err = rfcomm_connect(device, NULL)) < 0) { - error("Unable to connect"); - if (c->msg) - err_connect_failed(device->conn, c->msg, - strerror(-err)); + err = rfcomm_connect(device, NULL); + if (err < 0) { + error("Unable to connect: %s (%s)", strerror(-err), -err); + c->err = -err; goto failed; } @@ -671,8 +672,11 @@ static void get_record_reply(DBusPendingCall *call, void *data) return; failed_not_supported: - if (c->msg) + if (c->msg) { err_not_supported(device->conn, c->msg); + dbus_message_unref(c->msg); + c->msg = NULL; + } failed: if (classes) sdp_list_free(classes, free); @@ -783,6 +787,11 @@ static void get_handles_reply(DBusPendingCall *call, void *data) failed: if (msg) dbus_message_unref(msg); + /* The reply was already sent above */ + if (c->msg) { + dbus_message_unref(c->msg); + c->msg = NULL; + } dbus_message_unref(reply); g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); -- cgit From a13f569db6e188218919014e97e37e457f5940ee Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 3 Oct 2007 13:54:14 +0000 Subject: Minor coding style fixes --- audio/headset.c | 6 ++---- audio/sink.c | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 5759ebe0..700149ee 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1246,8 +1246,7 @@ done: "SpeakerGainChanged", DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - } - else { + } else { hs->mic_gain = gain; dbus_connection_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, @@ -1554,8 +1553,7 @@ void headset_set_state(struct device *dev, headset_state_t state) AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); - } - else { + } else { close_sco(dev); dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, diff --git a/audio/sink.c b/audio/sink.c index e249af18..18bf7e37 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -148,8 +148,7 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, reply = dbus_message_new_method_return(pending->msg); send_message_and_unref(pending->conn, reply); debug("Stream successfully created"); - } - else { + } else { err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; -- cgit From 9232035b60c0d88126c1023a74bf4221c8c127ef Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 4 Oct 2007 11:12:40 +0000 Subject: Fix stream callback type --- audio/unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index cf225ae5..c77f29bc 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -293,7 +293,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, - void *user_data) + void *user_data, struct avdtp_error *err) { struct unix_client *client = user_data; char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)]; -- cgit From a8b6f0d51cbae34d78acb664c96b8e54a25b7640 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 8 Oct 2007 00:21:54 +0000 Subject: respond to alsa delay signal (players were getting stuck at the end of tracks) --- audio/pcm_bluetooth.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index fb3fce13..893a234c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -764,6 +764,25 @@ done: return ret; } +static int bluetooth_playback_delay(snd_pcm_ioplug_t *io, + snd_pcm_sframes_t *delayp) +{ + DBG(""); + + /* This updates io->hw_ptr value using pointer() function */ + snd_pcm_hwsync(io->pcm); + + *delayp = io->appl_ptr - io->hw_ptr; + if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) { + io->callback->stop(io); + io->state = SND_PCM_STATE_XRUN; + *delayp = 0; + } + /* This should never fail, ALSA API is really not + prepared to handle a non zero return value */ + return 0; +} + static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .start = bluetooth_playback_start, .stop = bluetooth_playback_stop, @@ -774,6 +793,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .transfer = bluetooth_hsp_write, .poll_descriptors = bluetooth_playback_poll_descriptors, .poll_revents = bluetooth_playback_poll_revents, + .delay = bluetooth_playback_delay, }; static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { @@ -798,6 +818,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .transfer = bluetooth_a2dp_write, .poll_descriptors = bluetooth_playback_poll_descriptors, .poll_revents = bluetooth_playback_poll_revents, + .delay = bluetooth_playback_delay, }; static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { -- cgit From 40bd7f5accc683091d836c3e820b1e05adbd7f06 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 8 Oct 2007 00:31:35 +0000 Subject: make it obvious the audo service defaults to not autostart (other .service files have this) --- audio/audio.service | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/audio.service b/audio/audio.service index 3db5aa32..1638cfff 100644 --- a/audio/audio.service +++ b/audio/audio.service @@ -2,3 +2,4 @@ Identifier=audio Name=Audio service Description=Bluetooth Audio service +Autostart=false -- cgit From af44f3cafbe4a2a2f2c4bb5879d803f766cf775a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 9 Oct 2007 13:31:57 +0000 Subject: Fix device removal when it is not the current adapter. --- audio/device.c | 5 +++-- audio/device.h | 1 + audio/manager.c | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 9c90ed43..9b76e019 100644 --- a/audio/device.c +++ b/audio/device.c @@ -301,6 +301,7 @@ struct device *device_register(DBusConnection *conn, dev->path = g_strdup(path); bacpy(&dev->dst, bda); bacpy(&dev->src, &src); + bacpy(&dev->store, &src); dev->conn = dbus_connection_ref(conn); return dev; @@ -317,7 +318,7 @@ int device_store(struct device *dev, gboolean is_default) return -EINVAL; ba2str(&dev->dst, dst_addr); - ba2str(&dev->src, src_addr); + ba2str(&dev->store, src_addr); create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); @@ -356,7 +357,7 @@ int device_remove_stored(struct device *dev) char src_addr[18], dst_addr[18]; ba2str(&dev->dst, dst_addr); - ba2str(&dev->src, src_addr); + ba2str(&dev->store, src_addr); create_name(filename, PATH_MAX, STORAGEDIR, src_addr, "audio"); diff --git a/audio/device.h b/audio/device.h index a9954314..61ccc677 100644 --- a/audio/device.h +++ b/audio/device.h @@ -55,6 +55,7 @@ struct device { DBusConnection *conn; char *adapter_path; char *path; + bdaddr_t store; bdaddr_t src; bdaddr_t dst; diff --git a/audio/manager.c b/audio/manager.c index e1fb81ce..f5a67cac 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1024,6 +1024,7 @@ static DBusSignalVTable manager_signals[] = { static void parse_stored_devices(char *key, char *value, void *data) { + bdaddr_t *src = data; struct device *device; bdaddr_t dst; @@ -1042,6 +1043,9 @@ static void parse_stored_devices(char *key, char *value, void *data) if (!device) return; + /* Change storage to source adapter */ + bacpy(&device->store, src); + if (strstr(value, "headset")) device->headset = headset_init(device, NULL, 0); if (strstr(value, "sink")) -- cgit From d328981381cec52309a57b991bad0b832b4b9373 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 9 Oct 2007 13:36:33 +0000 Subject: Handle cross-connect case for AVDTP --- audio/avdtp.c | 17 +++++++++++++++++ audio/avdtp.h | 4 +++- audio/sink.c | 41 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ee1bd06d..0fb64bb0 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -499,6 +499,23 @@ void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id) } } +avdtp_error_type_t avdtp_error_type(struct avdtp_error *err) +{ + return err->type; +} + +int avdtp_error_error_code(struct avdtp_error *err) +{ + assert(err->type == AVDTP_ERROR_ERROR_CODE); + return err->err.error_code; +} + +int avdtp_error_posix_errno(struct avdtp_error *err) +{ + assert(err->type == AVDTP_ERROR_ERRNO); + return err->err.posix_errno; +} + static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, uint8_t rseid) { diff --git a/audio/avdtp.h b/audio/avdtp.h index 6af47e57..a14355b9 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -253,7 +253,9 @@ avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); const char *avdtp_strerror(struct avdtp_error *err); -int avdtp_error_code(struct avdtp_error *err); +avdtp_error_type_t avdtp_error_type(struct avdtp_error *err); +int avdtp_error_error_code(struct avdtp_error *err); +int avdtp_error_posix_errno(struct avdtp_error *err); void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); diff --git a/audio/sink.c b/audio/sink.c index 18bf7e37..0c5391f3 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -43,6 +43,8 @@ #include "error.h" #include "sink.h" +#define STREAM_SETUP_RETRY_TIMER 2000 + struct pending_request { DBusConnection *conn; DBusMessage *msg; @@ -133,6 +135,27 @@ static void stream_state_changed(struct avdtp_stream *stream, sink->state = new_state; } +static gboolean stream_setup_retry(gpointer user_data) +{ + struct sink *sink = user_data; + struct pending_request *pending = sink->connect; + + if (sink->state >= AVDTP_STATE_OPEN) { + DBusMessage *reply; + debug("Stream successfully created, after XCASE connect:connect"); + reply = dbus_message_new_method_return(pending->msg); + send_message_and_unref(pending->conn, reply); + } else { + debug("Stream setup failed, after XCASE connect:connect"); + err_failed(pending->conn, pending->msg, "Stream setup failed"); + } + + sink->connect = NULL; + pending_request_free(pending); + + return FALSE; +} + static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, void *user_data, struct avdtp_error *err) @@ -141,21 +164,29 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct pending_request *pending; pending = sink->connect; - sink->connect = NULL; if (stream) { DBusMessage *reply; + sink->connect = NULL; reply = dbus_message_new_method_return(pending->msg); send_message_and_unref(pending->conn, reply); + pending_request_free(pending); debug("Stream successfully created"); } else { - err_failed(pending->conn, pending->msg, "Stream setup failed"); avdtp_unref(sink->session); sink->session = NULL; - debug("Stream setup failed : %s", avdtp_strerror(err)); + if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO + && avdtp_error_posix_errno(err) != EHOSTDOWN) { + debug("connect:connect XCASE detected"); + g_timeout_add(STREAM_SETUP_RETRY_TIMER, + stream_setup_retry, sink); + } else { + sink->connect = NULL; + err_failed(pending->conn, pending->msg, "Stream setup failed"); + pending_request_free(pending); + debug("Stream setup failed : %s", avdtp_strerror(err)); + } } - - pending_request_free(pending); } static DBusHandlerResult sink_connect(DBusConnection *conn, -- cgit From d0f7d2f3742cbd962a4e8ca7f982185fd07a424e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 10 Oct 2007 09:13:30 +0000 Subject: Remove suspend timer if the other end sends a suspend command --- audio/a2dp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index cb43dce2..e2aeedfd 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -680,6 +680,12 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, debug("SBC Sink: Suspend_Ind"); else debug("SBC Source: Suspend_Ind"); + + if (a2dp_sep->suspend_timer) { + g_source_remove(a2dp_sep->suspend_timer); + a2dp_sep->suspend_timer = NULL; + } + return TRUE; } -- cgit From 86898be87d24991e48d783b3e20696ff1ce62abc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 10 Oct 2007 09:14:07 +0000 Subject: Fix warning caused by previous patch --- audio/a2dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index e2aeedfd..b6f0c627 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -683,7 +683,7 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, if (a2dp_sep->suspend_timer) { g_source_remove(a2dp_sep->suspend_timer); - a2dp_sep->suspend_timer = NULL; + a2dp_sep->suspend_timer = 0; } return TRUE; -- cgit From 8cfa0ed821e386e1fb8e31f16522f7be67ba1d31 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 10 Oct 2007 09:16:31 +0000 Subject: Send error response in case of unsupported commands --- audio/headset.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 700149ee..9abc824d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -177,14 +177,16 @@ static headset_event_t parse_headset_event(const char *buf, char *rsp, buf += 2; - snprintf(rsp, rsp_len, "\r\nOK\r\n"); - - if (!strncmp(buf, "+CKPD", 5)) + if (!strncmp(buf, "+CKPD", 5)) { + snprintf(rsp, rsp_len, "\r\nOK\r\n"); return HEADSET_EVENT_KEYPRESS; - else if (!strncmp(buf, "+VG", 3)) + } else if (!strncmp(buf, "+VG", 3)) { + snprintf(rsp, rsp_len, "\r\nOK\r\n"); return HEADSET_EVENT_GAIN; - else + } else { + snprintf(rsp, rsp_len, "\r\nERROR\r\n"); return HEADSET_EVENT_UNKNOWN; + } } static void close_sco(struct device *device) -- cgit From 8ca5098af0bd09cedf8299ee87def134ed6162e4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Oct 2007 13:42:59 +0000 Subject: Remove stream idle timer --- audio/avdtp.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 0fb64bb0..1ffabd94 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -74,7 +74,6 @@ #define REQ_TIMEOUT 4000 #define DISCONNECT_TIMEOUT 5000 -#define STREAM_TIMEOUT 20000 typedef enum { AVDTP_SESSION_STATE_DISCONNECTED, @@ -318,7 +317,6 @@ struct avdtp_stream { the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ - guint idle_timer; }; /* Structure describing an AVDTP connection between two devices */ @@ -571,17 +569,6 @@ static void stream_free(struct avdtp_stream *stream) g_free(stream); } -static gboolean stream_timeout(struct avdtp_stream *stream) -{ - struct avdtp *session = stream->session; - - avdtp_close(session, stream); - - stream->idle_timer = 0; - - return FALSE; -} - static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { @@ -669,23 +656,12 @@ static void avdtp_sep_set_state(struct avdtp *session, switch (state) { case AVDTP_STATE_OPEN: - stream->idle_timer = g_timeout_add(STREAM_TIMEOUT, - (GSourceFunc) stream_timeout, - stream); break; case AVDTP_STATE_STREAMING: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; - } break; case AVDTP_STATE_IDLE: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; - } session->streams = g_slist_remove(session->streams, stream); if (session->pending_open == stream) handle_transport_connect(session, -1, 0); -- cgit From 7a89a8edbf7ef0a62257890e0b8f55e485bbf1a3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Oct 2007 15:23:04 +0000 Subject: Bring back the suspend timer but only set it for suspends initiated by us --- audio/avdtp.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 1ffabd94..8e2094bf 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -74,6 +74,7 @@ #define REQ_TIMEOUT 4000 #define DISCONNECT_TIMEOUT 5000 +#define STREAM_TIMEOUT 20000 typedef enum { AVDTP_SESSION_STATE_DISCONNECTED, @@ -317,6 +318,7 @@ struct avdtp_stream { the transport channel */ gboolean open_acp; /* If we are in ACT role for Open */ gboolean close_int; /* If we are in INT role for Close */ + guint idle_timer; }; /* Structure describing an AVDTP connection between two devices */ @@ -569,6 +571,17 @@ static void stream_free(struct avdtp_stream *stream) g_free(stream); } +static gboolean stream_timeout(struct avdtp_stream *stream) +{ + struct avdtp *session = stream->session; + + avdtp_close(session, stream); + + stream->idle_timer = 0; + + return FALSE; +} + static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { @@ -660,8 +673,16 @@ static void avdtp_sep_set_state(struct avdtp *session, case AVDTP_STATE_STREAMING: case AVDTP_STATE_CLOSING: case AVDTP_STATE_ABORTING: + if (stream->idle_timer) { + g_source_remove(stream->idle_timer); + stream->idle_timer = 0; + } break; case AVDTP_STATE_IDLE: + if (stream->idle_timer) { + g_source_remove(stream->idle_timer); + stream->idle_timer = 0; + } session->streams = g_slist_remove(session->streams, stream); if (session->pending_open == stream) handle_transport_connect(session, -1, 0); @@ -1933,6 +1954,9 @@ static gboolean avdtp_suspend_resp(struct avdtp *session, avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + stream->idle_timer = g_timeout_add(STREAM_TIMEOUT, + (GSourceFunc) stream_timeout, stream); + if (sep->cfm && sep->cfm->suspend) sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); -- cgit From f6d1f79b0382b03dc01d3bb823c8c3eb70f82457 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 11 Oct 2007 15:28:15 +0000 Subject: Don't set suspend timer if the SEP is locked --- audio/a2dp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index b6f0c627..a91141d7 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -633,7 +633,8 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, a2dp_sep->session = avdtp_ref(session); - a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, + if (!a2dp_sep->locked) + a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, (GSourceFunc) suspend_timeout, a2dp_sep); return TRUE; -- cgit From cde9331a505b7477f2b4f70abbc5cf577c60ae48 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 17 Oct 2007 12:45:56 +0000 Subject: Misc. headset fixes --- audio/headset.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 9abc824d..f2bc1711 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -63,14 +63,18 @@ #define HEADSET_GAIN_SPEAKER 'S' #define HEADSET_GAIN_MICROPHONE 'M' -static char *str_state[] = {"DISCONNECTED", "CONNECTING", "CONNECTED", - "STREAM_STARTING", "STREAMING"}; +static char *str_state[] = { + "HEADSET_STATE_DISCONNECTED", + "HEADSET_STATE_CONNECT_IN_PROGRESS", + "HEADSET_STATE_CONNECTED", + "HEADSET_STATE_PLAY_IN_PROGRESS", + "HEADSET_STATE_PLAYING", + }; struct pending_connect { DBusMessage *msg; DBusPendingCall *call; GIOChannel *io; - guint io_id; int sock; int err; unsigned int id; @@ -491,9 +495,9 @@ static int sco_connect(struct device *device, struct pending_connect *c) return -err; } - c->io_id = g_io_add_watch(c->io, - G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, device); + g_io_add_watch(c->io, + G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + (GIOFunc) sco_connect_cb, device); } else do_callback = TRUE; @@ -1546,6 +1550,7 @@ void headset_set_state(struct device *dev, headset_state_t state) case HEADSET_STATE_CONNECT_IN_PROGRESS: break; case HEADSET_STATE_CONNECTED: + close_sco(dev); if (hs->state < state) { g_io_add_watch(hs->rfcomm, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, @@ -1555,8 +1560,7 @@ void headset_set_state(struct device *dev, headset_state_t state) AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); - } else { - close_sco(dev); + } else if (hs->state == HEADSET_STATE_PLAYING) { dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Stopped", -- cgit From 3e1e3e3679cbe30eb8a2cf7905d709b658c4c1ad Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 18 Oct 2007 08:32:37 +0000 Subject: Fix poll timeout to avoid unnecessary polls --- audio/pcm_bluetooth.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 893a234c..cb440166 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -167,12 +167,17 @@ static void *playback_hw_thread(void *param) double period_time; struct timeval start; struct pollfd fds[2]; + int poll_timeout; fds[0] = data->server; fds[1] = data->stream; prev_periods = 0; period_time = 1000000.0 * data->io.period_size / data->io.rate; + if (period_time > (int) (MIN_PERIOD_TIME * 1000)) + poll_timeout = (int) (period_time / 1000.0f); + else + poll_timeout = MIN_PERIOD_TIME; gettimeofday(&start, 0); @@ -213,7 +218,8 @@ static void *playback_hw_thread(void *param) } iter_sleep: - ret = poll(fds, 2, MIN_PERIOD_TIME); + /* sleep up to one period interval */ + ret = poll(fds, 2, poll_timeout); if (ret < 0) { SNDERR("poll error: %s (%d)", strerror(errno), errno); if (errno != EINTR) -- cgit From 83a8b201c5316ef297f4630ac8ece0f3dd198ff0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 18 Oct 2007 08:36:36 +0000 Subject: Fix alsa buffer constraints --- audio/pcm_bluetooth.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index cb440166..806ed37b 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -45,7 +45,8 @@ #define MIN_PERIOD_TIME 1 -#define BUFFER_SIZE 2048 +#define MIN_BUFFER_SIZE 256 /* minimum size of buffer */ +#define MAX_BUFFER_SIZE 16384 /* allocated RAM for buffer */ #ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) @@ -123,7 +124,7 @@ struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ int samples; /* Number of encoded samples */ - uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + uint8_t buffer[MAX_BUFFER_SIZE];/* Codec transfer buffer */ int count; /* Codec transfer buffer counter */ int nsamples; /* Cumulative number of codec samples */ @@ -137,7 +138,7 @@ struct bluetooth_data { struct ipc_data_cfg cfg; /* Bluetooth device config */ struct pollfd stream; /* Audio stream filedescriptor */ struct pollfd server; /* Audio daemon filedescriptor */ - uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + uint8_t buffer[MAX_BUFFER_SIZE];/* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ @@ -938,14 +939,28 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; - /* supported block size */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - a2dp->codesize, a2dp->codesize); + /* supported block sizes: + * - lower limit is A2DP codec size + * - total buffer size is the upper limit (with two periods) */ + err = snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_PERIOD_BYTES, + a2dp->codesize, + MAX_BUFFER_SIZE / 2); + if (err < 0) + return err; + + /* supported buffer sizes */ + err = snd_pcm_ioplug_set_param_minmax(io, + SND_PCM_IOPLUG_HW_BUFFER_BYTES, + MIN_BUFFER_SIZE, + MAX_BUFFER_SIZE); if (err < 0) return err; + /* supported period count: + * - derived from max buffer size and minimum period size */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 50); + 2, MAX_BUFFER_SIZE / a2dp->codesize); if (err < 0) return err; -- cgit From fcbcc7bfdfe61d57f38bd44cbc7a2edcea23e7a8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 21:45:14 +0000 Subject: Add gstsbcutil.c and gstsbcutil.h. --- audio/Makefile.am | 3 +- audio/gstsbcutil.c | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/gstsbcutil.h | 47 ++++++++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 audio/gstsbcutil.c create mode 100644 audio/gstsbcutil.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index c54a8e72..13ed4080 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -43,7 +43,8 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ - gsta2dpsink.h gsta2dpsink.c + gsta2dpsink.h gsta2dpsink.c \ + gstsbcutil.h gstsbcutil.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c new file mode 100644 index 00000000..706e7f54 --- /dev/null +++ b/audio/gstsbcutil.c @@ -0,0 +1,185 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/* common functions for gstreamer sbc related plugins */ + + +#include "gstsbcutil.h" +#include "ipc.h" + +/* + * Selects one rate from a list of possible rates + * TODO - use a better approach to this (it is selecting the last element) + */ +gint gst_sbc_select_rate_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_int(gst_value_list_get_value(value, size-1)); +} + +/* + * Selects one rate from a range of possible rates + * TODO - use a better approach to this (it is selecting the maximum value) + */ +gint gst_sbc_select_rate_from_range(const GValue *value) +{ + return gst_value_get_int_range_max(value); +} + +/* + * Selects one number of channels from a list of possible numbers + * TODO - use a better approach to this (it is selecting the last element) + */ +gint gst_sbc_select_channels_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_int(gst_value_list_get_value(value, size-1)); +} + +/* + * Selects one number of channels option from a range of possible numbers + * TODO - use a better approach to this (it is selecting the maximum value) + */ +gint gst_sbc_select_channels_from_range(const GValue *value) +{ + return gst_value_get_int_range_max(value); +} + +/* + * Selects one number of blocks from a list of possible blocks + * TODO - use a better approach to this (it is selecting the last element) + */ +gint gst_sbc_select_blocks_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_int(gst_value_list_get_value(value, size-1)); +} + +/* + * Selects one blocks option from a range of possible blocks + * TODO - use a better approach to this (it is selecting the maximum value) + */ +gint gst_sbc_select_blocks_from_range(const GValue *value) +{ + return gst_value_get_int_range_max(value); +} + +/* + * Selects one number of subbands from a list + * TODO - use a better approach to this (it is selecting the last element) + */ +gint gst_sbc_select_subbands_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_int(gst_value_list_get_value(value, size-1)); +} + +/* + * Selects one subbands option from a range + * TODO - use a better approach to this (it is selecting the maximum value) + */ +gint gst_sbc_select_subbands_from_range(const GValue *value) +{ + return gst_value_get_int_range_max(value); +} + +/* + * Selects one allocation mode from the ones on the list + * TODO - use a better approach + */ +const gchar* gst_sbc_get_allocation_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_string(gst_value_list_get_value(value, size-1)); +} + +/* + * Selects one mode from the ones on the list + * TODO - use a better aproach + */ +const gchar* gst_sbc_get_mode_from_list(const GValue *value) +{ + guint size = gst_value_list_get_size(value); + return g_value_get_string(gst_value_list_get_value(value, size-1)); +} + +gint gst_sbc_get_allocation_mode_int(const gchar* allocation) +{ + if (g_ascii_strcasecmp(allocation, "loudness") == 0) + return CFG_ALLOCATION_LOUDNESS; + else if (g_ascii_strcasecmp(allocation, "snr") == 0) + return CFG_ALLOCATION_SNR; + else if (g_ascii_strcasecmp(allocation, "auto") == 0) + return CFG_ALLOCATION_AUTO; + else + return -1; +} + +gint gst_sbc_get_mode_int(const gchar* mode) +{ + if (g_ascii_strcasecmp(mode, "joint") == 0) + return CFG_MODE_JOINT_STEREO; + else if (g_ascii_strcasecmp(mode, "stereo") == 0) + return CFG_MODE_STEREO; + else if (g_ascii_strcasecmp(mode, "dual") == 0) + return CFG_MODE_DUAL_CHANNEL; + else if (g_ascii_strcasecmp(mode, "mono") == 0) + return CFG_MODE_MONO; + else if (g_ascii_strcasecmp(mode, "auto") == 0) + return CFG_MODE_AUTO; + else + return -1; +} + +const gchar* gst_sbc_get_mode_string(int joint) +{ + switch (joint) { + case CFG_MODE_MONO: + return "mono"; + case CFG_MODE_DUAL_CHANNEL: + return "dual"; + case CFG_MODE_STEREO: + return "stereo"; + case CFG_MODE_JOINT_STEREO: + return "joint"; + case CFG_MODE_AUTO: + return NULL; /* TODO what should be selected here? */ + default: + return NULL; + } +} + +const gchar* gst_sbc_get_allocation_string(int alloc) +{ + switch (alloc) { + case CFG_ALLOCATION_LOUDNESS: + return "loudness"; + case CFG_ALLOCATION_SNR: + return "snr"; + case CFG_ALLOCATION_AUTO: + return NULL; /* TODO what should be selected here? */ + default: + return NULL; + } +} diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h new file mode 100644 index 00000000..84161d85 --- /dev/null +++ b/audio/gstsbcutil.h @@ -0,0 +1,47 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/* common functions for gstreamer sbc related plugins */ + +#include + +gint gst_sbc_select_rate_from_list(const GValue *value); +gint gst_sbc_select_rate_from_range(const GValue *value); + +gint gst_sbc_select_channels_from_list(const GValue *value); +gint gst_sbc_select_channels_from_range(const GValue *value); + +gint gst_sbc_select_blocks_from_list(const GValue *value); +gint gst_sbc_select_blocks_from_range(const GValue *value); + +gint gst_sbc_select_subbands_from_list(const GValue *value); +gint gst_sbc_select_subbands_from_range(const GValue *value); + +const gchar* gst_sbc_get_allocation_from_list(const GValue *value); +gint gst_sbc_get_allocation_mode_int(const gchar* allocation); +const gchar* gst_sbc_get_allocation_string(int alloc); + +const gchar* gst_sbc_get_mode_from_list(const GValue *value); +gint gst_sbc_get_mode_int(const gchar* mode); +const gchar* gst_sbc_get_mode_string(int joint); -- cgit From 5dee78747cac71ad970b513dc229712be1348290 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 21:46:49 +0000 Subject: Fixes for gstsbcenc. --- audio/gstsbcenc.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++------ audio/gstsbcenc.h | 5 ++ 2 files changed, 146 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 812c1193..ea97563e 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -28,6 +28,13 @@ #include #include "gstsbcenc.h" +#include "gstsbcutil.h" + + +#define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO +#define SBC_ENC_DEFAULT_BLOCKS 16 +#define SBC_ENC_DEFAULT_SUB_BANDS 8 +#define SBC_ENC_DEFAULT_ALLOCATION CFG_ALLOCATION_AUTO GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug @@ -52,9 +59,31 @@ static GType gst_sbc_mode_get_type(void) return sbc_mode_type; } +#define GST_TYPE_SBC_ALLOCATION (gst_sbc_allocation_get_type()) + +static GType gst_sbc_allocation_get_type(void) +{ + static GType sbc_allocation_type = 0; + static GEnumValue sbc_allocations[] = { + { CFG_ALLOCATION_AUTO, "Auto", "auto" }, + { CFG_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, + { CFG_ALLOCATION_SNR, "SNR", "snr" }, + { -1, NULL, NULL} + }; + + if (!sbc_allocation_type) + sbc_allocation_type = g_enum_register_static( + "GstSbcAllocation", sbc_allocations); + + return sbc_allocation_type; +} + enum { PROP_0, PROP_MODE, + PROP_ALLOCATION, + PROP_BLOCKS, + PROP_SUB_BANDS }; GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); @@ -85,27 +114,81 @@ static GstStaticPadTemplate sbc_enc_src_factory = "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }")); + +static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) +{ + gint rate; + gint channels; + GstCaps* src_caps; + GstStructure *structure; + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_get_int (structure, "rate", &rate)) + return NULL; + if (!gst_structure_get_int (structure, "channels", &channels)) + return NULL; + + enc->sbc.rate = rate; + enc->sbc.channels = channels; + + if (enc->mode == 0) + enc->sbc.joint = CFG_MODE_JOINT_STEREO; + else + enc->sbc.joint = enc->mode; + + enc->sbc.blocks = enc->blocks; + enc->sbc.subbands = enc->subbands; + if (enc->allocation == 0) + enc->sbc.allocation = CFG_ALLOCATION_LOUDNESS; + else + enc->sbc.allocation = enc->allocation; + + src_caps = gst_caps_new_simple("audio/x-sbc", "rate", G_TYPE_INT, + enc->sbc.rate, "channels", G_TYPE_INT, + enc->sbc.channels, "mode", G_TYPE_STRING, + gst_sbc_get_mode_string(enc->sbc.joint), "subbands", + G_TYPE_INT, enc->sbc.subbands, "blocks", G_TYPE_INT, + enc->sbc.blocks, "allocation", G_TYPE_STRING, + gst_sbc_get_allocation_string(enc->sbc.allocation), + NULL); + + return src_caps; +} + +static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstSbcEnc *enc; + GstStructure *structure; + GstCaps *othercaps; + + enc = GST_SBC_ENC(GST_PAD_PARENT (pad)); + structure = gst_caps_get_structure(caps, 0); + + othercaps = sbc_enc_generate_srcpad_caps(enc, caps); + if (othercaps == NULL) + goto error; + + gst_pad_set_caps (enc->srcpad, othercaps); + gst_caps_unref(othercaps); + + return TRUE; + +error: + GST_ERROR_OBJECT (enc, "invalid input caps"); + return FALSE; +} + static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) { GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); GstFlowReturn res = GST_FLOW_OK; - GstStructure *structure; - gint rate, channels; guint size, offset = 0; guint8 *data; data = GST_BUFFER_DATA(buffer); size = GST_BUFFER_SIZE(buffer); - structure = gst_caps_get_structure(GST_PAD_CAPS(pad), 0); - gst_structure_get_int(structure, "rate", &rate); - gst_structure_get_int(structure, "channels", &channels); - - enc->sbc.rate = rate; - enc->sbc.channels = channels; - enc->sbc.subbands = 8; - enc->sbc.joint = 0; - while (offset < size) { GstBuffer *output; GstCaps *caps; @@ -184,11 +267,23 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id, { GstSbcEnc *enc = GST_SBC_ENC(object); + /* TODO - CAN ONLY BE CHANGED ON READY AND BELOW */ + switch (prop_id) { case PROP_MODE: enc->mode = g_value_get_enum(value); break; - + case PROP_ALLOCATION: + enc->allocation = g_value_get_enum(value); + break; + case PROP_BLOCKS: + /* TODO - verify consistency */ + enc->blocks = g_value_get_int(value); + break; + case PROP_SUB_BANDS: + /* TODO - verify consistency */ + enc->subbands = g_value_get_int(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -204,7 +299,15 @@ static void gst_sbc_enc_get_property(GObject *object, guint prop_id, case PROP_MODE: g_value_set_enum(value, enc->mode); break; - + case PROP_ALLOCATION: + g_value_set_enum(value, enc->allocation); + break; + case PROP_BLOCKS: + g_value_set_int(value, enc->blocks); + break; + case PROP_SUB_BANDS: + g_value_set_int(value, enc->subbands); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -224,8 +327,24 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); g_object_class_install_property(object_class, PROP_MODE, - g_param_spec_enum("mode", "Mode", "Encoding mode", - GST_TYPE_SBC_MODE, 0, G_PARAM_READWRITE)); + g_param_spec_enum("mode", "Mode", + "Encoding mode", GST_TYPE_SBC_MODE, + SBC_ENC_DEFAULT_MODE, G_PARAM_READWRITE)); + + g_object_class_install_property(object_class, PROP_ALLOCATION, + g_param_spec_enum("allocation", "Allocation", + "Allocation mode", GST_TYPE_SBC_ALLOCATION, + SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE)); + + g_object_class_install_property(object_class, PROP_BLOCKS, + g_param_spec_int("blocks", "Blocks", + "Blocks", 0, G_MAXINT, + SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); + + g_object_class_install_property(object_class, PROP_SUB_BANDS, + g_param_spec_int("subbands", "Sub Bands", + "Sub Bands", 0, G_MAXINT, + SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, "SBC encoding element"); @@ -234,9 +353,16 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) { self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink"); + gst_pad_set_setcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (sbc_enc_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + + self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; + self->blocks = SBC_ENC_DEFAULT_BLOCKS; + self->mode = SBC_ENC_DEFAULT_MODE; + self->allocation = SBC_ENC_DEFAULT_ALLOCATION; } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index e7daf2d3..0a95bcef 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -24,6 +24,8 @@ #include #include "sbc.h" +#include "ipc.h" + G_BEGIN_DECLS @@ -48,6 +50,9 @@ struct _GstSbcEnc { GstPad *srcpad; gint mode; + gint blocks; + gint allocation; + gint subbands; sbc_t sbc; }; -- cgit From cee061308a8d4730b13d308b1ee8fbf3e4a22578 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 21:47:53 +0000 Subject: Fixes sbcparser element. --- audio/gstsbcparse.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 5097a85f..e204870b 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -28,6 +28,7 @@ #include #include "gstsbcparse.h" +#include "gstsbcutil.h" GST_DEBUG_CATEGORY_STATIC(sbc_parse_debug); #define GST_CAT_DEFAULT sbc_parse_debug @@ -54,6 +55,170 @@ static GstStaticPadTemplate sbc_parse_src_factory = "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }")); +/* + Creates a fixed caps from the caps given. + +*/ +static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) +{ + GstCaps *result; + GstStructure *structure; + const GValue *value; + gboolean error = FALSE; + gint temp, rate, channels, blocks, subbands; + const gchar* allocation = NULL; + const gchar* mode = NULL; + const gchar* error_message = NULL; + + structure = gst_caps_get_structure(caps, 0); + + /* rate */ + if (!gst_structure_has_field(structure, "rate")) { + error = TRUE; + error_message = "no rate."; + goto error; + } else { + value = gst_structure_get_value(structure, "rate"); + if (GST_VALUE_HOLDS_LIST(value)) { + temp = gst_sbc_select_rate_from_list(value); + } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + temp = gst_sbc_select_rate_from_range(value); + } else { + temp = g_value_get_int(value); + } + rate = temp; + } + + /* channels */ + if (!gst_structure_has_field(structure, "channels")) { + error = TRUE; + error_message = "no channels."; + goto error; + } else { + value = gst_structure_get_value(structure, "channels"); + if (GST_VALUE_HOLDS_LIST(value)) { + temp = gst_sbc_select_channels_from_list(value); + } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + temp = gst_sbc_select_channels_from_range(value); + } else { + temp = g_value_get_int(value); + } + channels = temp; + } + + /* blocks */ + if (!gst_structure_has_field(structure, "blocks")) { + error = TRUE; + error_message = "no blocks."; + goto error; + } else { + value = gst_structure_get_value(structure, "blocks"); + if (GST_VALUE_HOLDS_LIST(value)) { + temp = gst_sbc_select_blocks_from_list(value); + } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + temp = gst_sbc_select_blocks_from_range(value); + } else { + temp = g_value_get_int(value); + } + blocks = temp; + } + + /* subbands */ + if (!gst_structure_has_field(structure, "subbands")) { + error = TRUE; + error_message = "no subbands."; + goto error; + } else { + value = gst_structure_get_value(structure, "subbands"); + if (GST_VALUE_HOLDS_LIST(value)) { + temp = gst_sbc_select_subbands_from_list(value); + } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + temp = gst_sbc_select_subbands_from_range(value); + } else { + temp = g_value_get_int(value); + } + subbands = temp; + } + + /* allocation */ + if (!gst_structure_has_field(structure, "allocation")) { + error = TRUE; + error_message = "no allocation."; + goto error; + } else { + value = gst_structure_get_value(structure, "allocation"); + if (GST_VALUE_HOLDS_LIST(value)) { + allocation = gst_sbc_get_allocation_from_list(value); + } else { + allocation = g_value_get_string(value); + } + } + + /* mode */ + if (!gst_structure_has_field(structure, "mode")) { + error = TRUE; + error_message = "no mode."; + goto error; + } else { + value = gst_structure_get_value(structure, "mode"); + if (GST_VALUE_HOLDS_LIST(value)) { + mode = gst_sbc_get_mode_from_list(value); + } else { + mode = g_value_get_string(value); + } + } + +error: + if (error) { + GST_ERROR_OBJECT (parse, "Invalid input caps: %s", + error_message); + return NULL; + } + + + result = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, rate, + "channels", G_TYPE_INT, channels, + "mode", G_TYPE_STRING, mode, + "blocks", G_TYPE_INT, blocks, + "subbands", G_TYPE_INT, subbands, + "allocation", G_TYPE_STRING, allocation, + NULL); + parse->sbc.rate = rate; + parse->sbc.channels = channels; + parse->sbc.blocks = blocks; + parse->sbc.subbands = subbands; + parse->sbc.joint = gst_sbc_get_mode_int(mode); + parse->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); + + return result; +} + +static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) +{ + GstSbcParse *parse; + GstCaps *inter, *other, *srccaps; + + parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); + + other = gst_pad_peer_get_caps(parse->srcpad); + if (other == NULL) + other = gst_caps_new_any(); + + inter = gst_caps_intersect(caps, other); + srccaps = sbc_parse_select_caps(parse, inter); + if (srccaps == NULL) + return FALSE; + + gst_pad_set_caps(parse->srcpad, srccaps); + + gst_caps_unref(inter); + gst_caps_unref(other); + gst_caps_unref(srccaps); + + return TRUE; +} + static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) { GstSbcParse *parse = GST_SBC_PARSE(gst_pad_get_parent(pad)); @@ -78,31 +243,20 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) while (offset < size) { GstBuffer *output; - GstPadTemplate *template; - GstCaps *caps, *temp; + GstCaps *temp; int consumed; consumed = sbc_parse(&parse->sbc, data + offset, size - offset); if (consumed <= 0) break; - caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, parse->sbc.rate, - "channels", G_TYPE_INT, parse->sbc.channels, - NULL); - template = gst_static_pad_template_get(&sbc_parse_src_factory); - - temp = gst_caps_intersect(caps, - gst_pad_template_get_caps(template)); - - gst_caps_unref(caps); + temp = GST_PAD_CAPS(parse->srcpad); res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad, GST_BUFFER_OFFSET_NONE, consumed, temp, &output); - gst_caps_unref(temp); if (res != GST_FLOW_OK) goto done; @@ -141,7 +295,6 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, } sbc_init(&parse->sbc, 0); break; - case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG("Finish subband codec"); if (parse->buffer) { @@ -150,7 +303,6 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, } sbc_finish(&parse->sbc); break; - default: break; } @@ -180,13 +332,15 @@ static void gst_sbc_parse_class_init(GstSbcParseClass *klass) element_class->change_state = GST_DEBUG_FUNCPTR(sbc_parse_change_state); GST_DEBUG_CATEGORY_INIT(sbc_parse_debug, "sbcparse", 0, - "SBC parsing element"); + "SBC parsing element"); } static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) { self->sinkpad = gst_pad_new_from_static_template(&sbc_parse_sink_factory, "sink"); gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain)); + gst_pad_set_setcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (sbc_parse_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template(&sbc_parse_src_factory, "src"); -- cgit From 942422b8c90ba6e30339ab066cdc7fd2fc31cb7c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 21:50:00 +0000 Subject: Fixes a2dpsink element. --- audio/gsta2dpsink.c | 744 ++++++++++++++++++++++++++++++++++++++++++++++----- audio/gsta2dpsink.h | 28 +- audio/gstbluetooth.c | 2 - audio/sink.c | 2 +- 4 files changed, 703 insertions(+), 73 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 8a6a44f1..0e8c8e02 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -28,22 +28,128 @@ #include #include #include +#include +#include + +#include + +#include #include "ipc.h" +#include "sbc.h" #include "gsta2dpsink.h" GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug -#define DEFAULT_DEVICE "default" +#define BUFFER_SIZE 2048 + +#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START { \ + g_mutex_lock (s->sink_lock); \ + } G_STMT_END + +#define GST_A2DP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ + g_mutex_unlock (s->sink_lock); \ + } G_STMT_END + +#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START { \ + s->waiting_con_conf = TRUE; \ + g_cond_wait (s->con_conf_end, s->sink_lock); \ + s->waiting_con_conf = FALSE; \ + } G_STMT_END + +#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START { \ + s->con_state = NOT_CONFIGURED; \ + g_cond_signal (s->con_conf_end); \ + } G_STMT_END + +#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START { \ + s->con_state = CONFIGURED; \ + g_cond_signal (s->con_conf_end); \ + } G_STMT_END + +struct bluetooth_a2dp { + sbc_t sbc; /* Codec data */ + int codesize; /* SBC codesize */ + int samples; /* Number of encoded samples */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + int count; /* Codec transfer buffer counter */ + + int nsamples; /* Cumulative number of codec samples */ + uint16_t seq_num; /* Cumulative packet sequence */ + int frame_count; /* Current frames in buffer*/ +}; +struct bluetooth_data { + struct ipc_data_cfg cfg; /* Bluetooth device config */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + int count; /* Transfer buffer counter */ + struct bluetooth_a2dp a2dp; /* A2DP data */ +}; + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + uint8_t cc:4; + uint8_t x:1; + uint8_t p:1; + uint8_t v:2; + + uint8_t pt:7; + uint8_t m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t frame_count:4; + uint8_t rfa0:1; + uint8_t is_last_fragment:1; + uint8_t is_first_fragment:1; + uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + uint8_t v:2; + uint8_t p:1; + uint8_t x:1; + uint8_t cc:4; + + uint8_t m:1; + uint8_t pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t is_fragmented:1; + uint8_t is_first_fragment:1; + uint8_t is_last_fragment:1; + uint8_t rfa0:1; + uint8_t frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +#define IS_SBC(n) (strcmp(n, "audio/x-sbc") == 0) +#define IS_MPEG(n) (strcmp(n, "audio/mpeg") == 0) enum { PROP_0, PROP_DEVICE, }; -GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstAudioSink, GST_TYPE_AUDIO_SINK); +GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK); static const GstElementDetails a2dp_sink_details = GST_ELEMENT_DETAILS("Bluetooth A2DP sink", @@ -60,7 +166,6 @@ static GstStaticPadTemplate a2dp_sink_factory = "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }; " - "audio/mpeg, " "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ], " @@ -77,13 +182,54 @@ static void gst_a2dp_sink_base_init(gpointer g_class) gst_element_class_set_details(element_class, &a2dp_sink_details); } +static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + struct bluetooth_a2dp *a2dp = &self->data->a2dp; + + self->con_state = NOT_CONFIGURED; + self->total = 0; + + if (self->stream) { + g_io_channel_close(self->stream); + g_io_channel_unref(self->stream); + self->stream = NULL; + } + + if (self->server) { + g_io_channel_close(self->server); + g_io_channel_unref(self->server); + self->stream = NULL; + } + + if (self->data->cfg.codec == CFG_CODEC_SBC) + sbc_finish(&a2dp->sbc); + + if (self->data) { + g_free(self->data); + self->data = NULL; + } + + return TRUE; +} + static void gst_a2dp_sink_finalize(GObject *object) { - GstA2dpSink *sink = GST_A2DP_SINK(object); + GstA2dpSink *self = GST_A2DP_SINK(object); + + if (self->data) + gst_a2dp_sink_stop (GST_BASE_SINK (self)); + + if (self->device) + g_free(self->device); - g_io_channel_close(sink->server); + /* unlock any thread waiting for this signal */ + GST_A2DP_SINK_MUTEX_LOCK(self); + GST_A2DP_SINK_CONFIGURATION_FAIL(self); + GST_A2DP_SINK_MUTEX_UNLOCK(self); - g_free(sink->device); + g_cond_free(self->con_conf_end); + g_mutex_free(self->sink_lock); G_OBJECT_CLASS(parent_class)->finalize(object); } @@ -95,11 +241,9 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, switch (prop_id) { case PROP_DEVICE: - g_free(sink->device); + if (sink->device) + g_free(sink->device); sink->device = g_value_dup_string(value); - - if (sink->device == NULL) - sink->device = g_strdup(DEFAULT_DEVICE); break; default: @@ -124,114 +268,580 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, } } -static gboolean gst_a2dp_sink_open(GstAudioSink *self) +static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) { - GstA2dpSink *sink = GST_A2DP_SINK(self); + char cmsg_b[CMSG_SPACE(sizeof(int))], m; + int err, ret, stream_fd; + struct iovec iov = { &m, sizeof(m) }; + struct msghdr msgh; + struct cmsghdr *cmsg; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(sizeof(int)); + + ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0); + if (ret < 0) { + err = errno; + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + strerror(err), err); + return -err; + } - printf("device %s\n", sink->device); - printf("open\n"); + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + stream_fd = (*(int *) CMSG_DATA(cmsg)); + sink->stream = g_io_channel_unix_new(stream_fd); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd); + return 0; + } + } - return TRUE; + return -EINVAL; } -static gboolean gst_a2dp_sink_prepare(GstAudioSink *self, - GstRingBufferSpec *spec) +static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, + struct ipc_codec_sbc *sbc) { - printf("perpare\n"); - printf("rate %d\n", spec->rate); - printf("channels %d\n", spec->channels); + struct bluetooth_a2dp *a2dp = &sink->data->a2dp; + struct ipc_data_cfg *cfg = &sink->data->cfg; - return TRUE; + if (cfg == NULL) { + GST_ERROR_OBJECT(sink, "Error getting codec parameters"); + return -1; + } + + if (cfg->codec != CFG_CODEC_SBC) + return -1; + + /* FIXME: init using flags? */ + sbc_init(&a2dp->sbc, 0); + a2dp->sbc.rate = cfg->rate; + a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; + if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) + a2dp->sbc.joint = 1; + a2dp->sbc.allocation = sbc->allocation; + a2dp->sbc.subbands = sbc->subbands; + a2dp->sbc.blocks = sbc->blocks; + a2dp->sbc.bitpool = sbc->bitpool; + a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + + GST_DEBUG_OBJECT(sink, "Codec parameters: \ + \tallocation=%u\n\tsubbands=%u\n \ + \tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, + a2dp->sbc.blocks, a2dp->sbc.bitpool); + + return 0; } -static gboolean gst_a2dp_sink_unprepare(GstAudioSink *self) +static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, + GstCaps *caps, + struct ipc_packet *pkt) { - printf("unprepare\n"); + + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + const GValue *value = NULL; + const char *pref, *name; + GstStructure *structure = gst_caps_get_structure(caps,0); + + name = gst_structure_get_name(structure); + /* FIXME only sbc supported here, should suport mp3 */ + if (!(IS_SBC(name))) { + GST_ERROR_OBJECT(sink, "Unsupported format %s", name); + return FALSE; + } + + if (sink->device) + strncpy(pkt->device, sink->device, 18); + + pkt->role = PKT_ROLE_HIFI; + + value = gst_structure_get_value(structure, "rate"); + cfg->rate = g_value_get_int(value); + + value = gst_structure_get_value(structure, "mode"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->mode = CFG_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->mode = CFG_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->mode = CFG_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->mode = CFG_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->mode = CFG_MODE_JOINT_STEREO; + else { + GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "allocation"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + sbc->allocation = CFG_ALLOCATION_AUTO; + else if (strcmp(pref, "loudness") == 0) + sbc->allocation = CFG_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + sbc->allocation = CFG_ALLOCATION_SNR; + else { + GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "subbands"); + sbc->subbands = g_value_get_int(value); + + value = gst_structure_get_value(structure, "blocks"); + sbc->blocks = g_value_get_int(value); +/* FIXME how can I obtain the bitpool ? + if (strcmp(id, "bitpool") == 0) { + if (snd_config_get_string(n, &bitpool) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + sbc->bitpool = atoi(bitpool); + continue; + } + + SNDERR("Unknown field %s", id); + return -EINVAL; + } +*/ + sbc->bitpool = 32; + + pkt->length = sizeof(*cfg) + sizeof(*sbc); + pkt->type = PKT_TYPE_CFG_REQ; + pkt->error = PKT_ERROR_NONE; return TRUE; + } -static gboolean gst_a2dp_sink_close(GstAudioSink *self) +static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) { - printf("close\n"); + gchar buf[IPC_MTU]; + GIOError io_error; + guint ret; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + + memset(buf, 0, sizeof(buf)); + + io_error = g_io_channel_read(sink->server, (gchar*)buf, + sizeof(*pkt) + sizeof(*cfg), &ret); + if (io_error != G_IO_ERROR_NONE && ret > 0) { + GST_ERROR_OBJECT(sink, "Error ocurred while receiving \ + configurarion packet answer"); + return FALSE; + } + + sink->total = ret; + if (pkt->type != PKT_TYPE_CFG_RSP) { + GST_ERROR_OBJECT(sink, "Unexpected packet type %d \ + received", pkt->type); + return FALSE; + } + + if (pkt->error != PKT_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error %d while configuring \ + device", pkt->error); + return FALSE; + } + + if (cfg->codec != CFG_CODEC_SBC) { + GST_ERROR_OBJECT(sink, "Unsupported format"); + return FALSE; + } return TRUE; } -static guint gst_a2dp_sink_write(GstAudioSink *self, - gpointer data, guint length) +static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) { - return 0; + gchar buf[IPC_MTU]; + GIOError io_error; + guint ret=0; + struct ipc_packet *pkt = (void *) buf; + struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; + + io_error = g_io_channel_read(sink->server, (gchar*) sbc, + sizeof(*sbc), &ret); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error while reading data from socket \ + %s (%d)", strerror(errno), errno); + return FALSE; + } else if (ret == 0) { + GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); + return FALSE; + } + + sink->total += ret; + GST_DEBUG_OBJECT(sink, "OK - %d bytes received", sink->total); + + if (pkt->length != (sink->total - sizeof(struct ipc_packet))) { + GST_ERROR_OBJECT(sink, "Error while configuring device: \ + packet size doesn't match"); + return FALSE; + } + + memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); + + GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t\ + fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", + sink->stream, sink->data->cfg.fd_opt, \ + sink->data->cfg.pkt_len, sink->data->cfg.sample_size, \ + sink->data->cfg.rate); + + if (sink->data->cfg.codec == CFG_CODEC_SBC) { + ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); + if (ret < 0) + return FALSE; + + } + return TRUE; } -static guint gst_a2dp_sink_delay(GstAudioSink *audiosink) +static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *sink) { - printf("delay\n"); + gint ret; + GIOError err; + gsize read; + + ret = gst_a2dp_sink_bluetooth_recvmsg_fd(sink); + if (ret < 0) + return FALSE; + + if (!sink->stream) { + GST_ERROR_OBJECT(sink, "Error while configuring device: \ + could not acquire audio socket"); + return FALSE; + } - return 0; + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + while (TRUE) { + err = g_io_channel_read(sink->stream, + (gchar *) sink->data->buffer, + (gsize) sink->data->cfg.pkt_len, + &read); + if (err != G_IO_ERROR_NONE || read <= 0) + break; + } + + memset(sink->data->buffer, 0, sizeof(sink->data->buffer)); + + return TRUE; } -static void gst_a2dp_sink_reset(GstAudioSink *audiosink) +static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) { - printf("reset\n"); + /* + * We hold the lock, since we can send a signal. + * It is a good practice, according to the glib api. + */ + GST_A2DP_SINK_MUTEX_LOCK(sink); + + switch (sink->con_state) { + case CONFIGURING_SENT_CONF: + if (gst_a2dp_sink_conf_resp(sink)) + sink->con_state = CONFIGURING_RCVD_CONF_RSP; + else + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + break; + case CONFIGURING_RCVD_CONF_RSP: + if (gst_a2dp_sink_conf_recv_dev_conf(sink)) + sink->con_state = CONFIGURING_RCVD_DEV_CONF; + else + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + case CONFIGURING_RCVD_DEV_CONF: + if (gst_a2dp_sink_conf_recv_stream_fd(sink)) + GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); + else + GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + break; + } + + GST_A2DP_SINK_MUTEX_UNLOCK(sink); } + static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { - printf("callback\n"); + GstA2dpSink *sink = GST_A2DP_SINK(data); + + switch (cond) { + case G_IO_IN: + if (sink->con_state != NOT_CONFIGURED && + sink->con_state != CONFIGURED) + gst_a2dp_sink_conf_recv_data(sink); + else + GST_WARNING_OBJECT(sink, "Unexpected data received"); + break; + case G_IO_HUP: + return FALSE; + break; + case G_IO_ERR: + GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); + break; + case G_IO_NVAL: + return FALSE; + break; + default: + GST_WARNING_OBJECT(sink, "Unexpected callback call"); + break; + } return TRUE; } +static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; + gint sk; + gint err; + + sk = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sk < 0) { + err = errno; + GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)", + strerror(err), err); + return FALSE; + } + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + GST_ERROR_OBJECT(self, "Connection fail %s (%d)", + strerror(err), err); + close(sk); + return FALSE; + } + + self->server = g_io_channel_unix_new(sk); + + g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + server_callback, self); + + self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); + + return TRUE; +} + +static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) +{ + gchar buf[IPC_MTU]; + struct ipc_packet *pkt = (void *) buf; + gboolean ret; + guint bytes_sent; + GIOError io_error; + + g_assert(sink->con_state == NOT_CONFIGURED); + + memset (pkt, 0, sizeof(buf)); + ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); + if (!ret) { + GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps \ + to packet configuration"); + return FALSE; + } + + sink->con_state = CONFIGURING_INIT; + + io_error = g_io_channel_write(sink->server, (gchar*)pkt, + sizeof(*pkt) + pkt->length, &bytes_sent); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(sink, "Error ocurred while sending \ + configurarion packet"); + sink->con_state = NOT_CONFIGURED; + return FALSE; + } + + GST_DEBUG_OBJECT(sink, "%d bytes sent", bytes_sent); + sink->con_state = CONFIGURING_SENT_CONF; + + return TRUE; +} + +static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps) +{ + gboolean ret; + + g_assert(sink->con_state == NOT_CONFIGURED); + + GST_DEBUG_OBJECT(sink, "starting device configuration"); + + ret = gst_a2dp_sink_send_conf_pkt(sink, caps); + + return ret; +} + +static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSink *sink = GST_A2DP_SINK(basesink); + + GST_A2DP_SINK_MUTEX_LOCK(sink); + + if (sink->con_state == NOT_CONFIGURED) + gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer)); + + /* wait for the connection process to finish */ + if (sink->con_state != CONFIGURED) + GST_A2DP_SINK_WAIT_CON_END(sink); + + GST_A2DP_SINK_MUTEX_UNLOCK(sink); + + if (sink->con_state != CONFIGURED) + return GST_FLOW_ERROR; + + return GST_FLOW_OK; +} + +static int gst_a2dp_sink_avdtp_write(GstA2dpSink *sink) +{ + int ret = 0; + struct bluetooth_data *data; + struct rtp_header *header; + struct rtp_payload *payload; + struct bluetooth_a2dp *a2dp; + GIOError err; + + data = sink->data; + a2dp = &data->a2dp; + + header = (void *) a2dp->buffer; + payload = (void *) (a2dp->buffer + sizeof(*header)); + + memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); + + payload->frame_count = a2dp->frame_count; + header->v = 2; + header->pt = 1; + header->sequence_number = htons(a2dp->seq_num); + header->timestamp = htonl(a2dp->nsamples); + header->ssrc = htonl(1); + + while (TRUE) { + err = g_io_channel_write(sink->stream, (const char *) a2dp->buffer, + (gsize) a2dp->count, (gsize *) &ret); + + if (err == G_IO_ERROR_AGAIN) { + usleep (100); + continue; + } + + break; + } + + /* Reset buffer of data to send */ + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + a2dp->frame_count = 0; + a2dp->samples = 0; + a2dp->seq_num++; + + return ret; +} + +static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSink *sink; + struct bluetooth_data *data; + struct bluetooth_a2dp *a2dp; + gint encoded, frame_size=1024; + gint ret=0; + + sink = GST_A2DP_SINK(basesink); + data = (struct bluetooth_data*) sink->data; + a2dp = &data->a2dp; + + encoded = GST_BUFFER_SIZE(buffer); + + if (a2dp->count + encoded >= data->cfg.pkt_len) { + ret = gst_a2dp_sink_avdtp_write(sink); + if (ret < 0) + return GST_FLOW_ERROR; + } + + memcpy(a2dp->buffer + a2dp->count, GST_BUFFER_DATA(buffer), encoded); + a2dp->count += encoded; + a2dp->frame_count++; + a2dp->samples += encoded / frame_size; + a2dp->nsamples += encoded / frame_size; + + return GST_FLOW_OK; +} + +static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + + GST_A2DP_SINK_MUTEX_LOCK(self); + if (self->con_state == NOT_CONFIGURED) + gst_a2dp_sink_start_dev_conf(self, caps); + GST_A2DP_SINK_MUTEX_UNLOCK(self); + + return TRUE; +} + +static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) +{ + return TRUE; +} + static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstAudioSinkClass *audiosink_class = GST_AUDIO_SINK_CLASS(klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); parent_class = g_type_class_peek_parent(klass); object_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); - object_class->set_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_property); - - audiosink_class->open = GST_DEBUG_FUNCPTR(gst_a2dp_sink_open); - audiosink_class->prepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_prepare); - audiosink_class->unprepare = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unprepare); - audiosink_class->close = GST_DEBUG_FUNCPTR(gst_a2dp_sink_close); - audiosink_class->write = GST_DEBUG_FUNCPTR(gst_a2dp_sink_write); - audiosink_class->delay = GST_DEBUG_FUNCPTR(gst_a2dp_sink_delay); - audiosink_class->reset = GST_DEBUG_FUNCPTR(gst_a2dp_sink_reset); + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_get_property); + + basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sink_stop); + basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render); + basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); + basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); + basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device", - DEFAULT_DEVICE, G_PARAM_READWRITE)); + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, - "A2DP sink element"); + "A2DP sink element"); } static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { - struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; - int sk; + self->device = NULL; + self->data = NULL; - self->device = g_strdup(DEFAULT_DEVICE); - - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) - return; - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return; - } - - self->server = g_io_channel_unix_new(sk); - - g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - server_callback, self); + self->stream = NULL; + self->con_state = NOT_CONFIGURED; + self->total = 0; - g_io_channel_unref(self->server); + self->con_conf_end = g_cond_new(); + self->waiting_con_conf = FALSE; + self->sink_lock = g_mutex_new(); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index c83ec66c..6c7cfe2a 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -22,7 +22,7 @@ */ #include -#include +#include G_BEGIN_DECLS @@ -37,19 +37,41 @@ G_BEGIN_DECLS #define GST_IS_A2DP_SINK_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) +enum { + NOT_CONFIGURED, + CONFIGURING_INIT, + CONFIGURING_SENT_CONF, + CONFIGURING_RCVD_CONF_RSP, + CONFIGURING_RCVD_DEV_CONF, + CONFIGURED +}; + typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; +struct bluetooth_data; + struct _GstA2dpSink { - GstAudioSink sink; + GstBaseSink sink; gchar *device; + GIOChannel *stream; + struct bluetooth_data *data; GIOChannel *server; + + gint con_state; + + GCond *con_conf_end; + gboolean waiting_con_conf; + GMutex *sink_lock; + + gint total; + }; struct _GstA2dpSinkClass { - GstAudioSinkClass parent_class; + GstBaseSinkClass parent_class; }; GType gst_a2dp_sink_get_type(void); diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index ce065820..593a311e 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -67,11 +67,9 @@ static gboolean plugin_init(GstPlugin *plugin) GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE) return FALSE; -#if 0 if (gst_element_register(plugin, "a2dpsink", GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) return FALSE; -#endif return TRUE; } diff --git a/audio/sink.c b/audio/sink.c index 0c5391f3..2515c968 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -177,7 +177,7 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, sink->session = NULL; if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO && avdtp_error_posix_errno(err) != EHOSTDOWN) { - debug("connect:connect XCASE detected"); + debug("connect:connect XCASE detected"); g_timeout_add(STREAM_SETUP_RETRY_TIMER, stream_setup_retry, sink); } else { -- cgit From 2dda76438063672e2bdccf1709db3615d453ad5b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 22:46:12 +0000 Subject: Fix coding style issues. --- audio/gsta2dpsink.c | 49 +++++++++++++++++-------------------------------- audio/gstsbcenc.c | 30 +++++++++++++++++------------- audio/gstsbcparse.c | 2 ++ 3 files changed, 36 insertions(+), 45 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 0e8c8e02..1bb88b36 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -141,8 +141,8 @@ struct rtp_payload { #error "Unknown byte order" #endif -#define IS_SBC(n) (strcmp(n, "audio/x-sbc") == 0) -#define IS_MPEG(n) (strcmp(n, "audio/mpeg") == 0) +#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) +#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) enum { PROP_0, @@ -218,7 +218,7 @@ static void gst_a2dp_sink_finalize(GObject *object) GstA2dpSink *self = GST_A2DP_SINK(object); if (self->data) - gst_a2dp_sink_stop (GST_BASE_SINK (self)); + gst_a2dp_sink_stop(GST_BASE_SINK(self)); if (self->device) g_free(self->device); @@ -285,7 +285,7 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0); if (ret < 0) { err = errno; - GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", strerror(err), err); return -err; } @@ -305,7 +305,7 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) return -EINVAL; } -static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, +static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, struct ipc_codec_sbc *sbc) { struct bluetooth_a2dp *a2dp = &sink->data->a2dp; @@ -403,21 +403,6 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, value = gst_structure_get_value(structure, "blocks"); sbc->blocks = g_value_get_int(value); -/* FIXME how can I obtain the bitpool ? - if (strcmp(id, "bitpool") == 0) { - if (snd_config_get_string(n, &bitpool) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - sbc->bitpool = atoi(bitpool); - continue; - } - - SNDERR("Unknown field %s", id); - return -EINVAL; - } -*/ sbc->bitpool = 32; pkt->length = sizeof(*cfg) + sizeof(*sbc); @@ -438,7 +423,7 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) memset(buf, 0, sizeof(buf)); - io_error = g_io_channel_read(sink->server, (gchar*)buf, + io_error = g_io_channel_read(sink->server, (gchar *) buf, sizeof(*pkt) + sizeof(*cfg), &ret); if (io_error != G_IO_ERROR_NONE && ret > 0) { GST_ERROR_OBJECT(sink, "Error ocurred while receiving \ @@ -476,14 +461,14 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; - io_error = g_io_channel_read(sink->server, (gchar*) sbc, + io_error = g_io_channel_read(sink->server, (gchar *) sbc, sizeof(*sbc), &ret); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(sink, "Error while reading data from socket \ - %s (%d)", strerror(errno), errno); + %s (%d)", strerror(errno), errno); return FALSE; } else if (ret == 0) { - GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); + GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); return FALSE; } @@ -491,8 +476,8 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) GST_DEBUG_OBJECT(sink, "OK - %d bytes received", sink->total); if (pkt->length != (sink->total - sizeof(struct ipc_packet))) { - GST_ERROR_OBJECT(sink, "Error while configuring device: \ - packet size doesn't match"); + GST_ERROR_OBJECT(sink, "Error while configuring device: " + "packet size doesn't match"); return FALSE; } @@ -524,14 +509,14 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *sink) return FALSE; if (!sink->stream) { - GST_ERROR_OBJECT(sink, "Error while configuring device: \ - could not acquire audio socket"); + GST_ERROR_OBJECT(sink, "Error while configuring device: " + "could not acquire audio socket"); return FALSE; } /* It is possible there is some outstanding data in the pipe - we have to empty it */ - while (TRUE) { + while (1) { err = g_io_channel_read(sink->stream, (gchar *) sink->data->buffer, (gsize) sink->data->cfg.pkt_len, @@ -589,7 +574,7 @@ static gboolean server_callback(GIOChannel *chan, gst_a2dp_sink_conf_recv_data(sink); else GST_WARNING_OBJECT(sink, "Unexpected data received"); - break; + break; case G_IO_HUP: return FALSE; break; @@ -661,7 +646,7 @@ static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) sink->con_state = CONFIGURING_INIT; - io_error = g_io_channel_write(sink->server, (gchar*)pkt, + io_error = g_io_channel_write(sink->server, (gchar *) pkt, sizeof(*pkt) + pkt->length, &bytes_sent); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(sink, "Error ocurred while sending \ @@ -735,7 +720,7 @@ static int gst_a2dp_sink_avdtp_write(GstA2dpSink *sink) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - while (TRUE) { + while (1) { err = g_io_channel_write(sink->stream, (const char *) a2dp->buffer, (gsize) a2dp->count, (gsize *) &ret); diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index ea97563e..628d15c9 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -30,7 +30,6 @@ #include "gstsbcenc.h" #include "gstsbcutil.h" - #define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO #define SBC_ENC_DEFAULT_BLOCKS 16 #define SBC_ENC_DEFAULT_SUB_BANDS 8 @@ -83,7 +82,7 @@ enum { PROP_MODE, PROP_ALLOCATION, PROP_BLOCKS, - PROP_SUB_BANDS + PROP_SUBBANDS }; GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); @@ -121,6 +120,8 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) gint channels; GstCaps* src_caps; GstStructure *structure; + const gchar *mode; + const gchar *allocation; structure = gst_caps_get_structure(caps, 0); @@ -144,14 +145,17 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) else enc->sbc.allocation = enc->allocation; - src_caps = gst_caps_new_simple("audio/x-sbc", "rate", G_TYPE_INT, - enc->sbc.rate, "channels", G_TYPE_INT, - enc->sbc.channels, "mode", G_TYPE_STRING, - gst_sbc_get_mode_string(enc->sbc.joint), "subbands", - G_TYPE_INT, enc->sbc.subbands, "blocks", G_TYPE_INT, - enc->sbc.blocks, "allocation", G_TYPE_STRING, - gst_sbc_get_allocation_string(enc->sbc.allocation), - NULL); + mode = gst_sbc_get_mode_string(enc->sbc.joint); + allocation = gst_sbc_get_allocation_string(enc->sbc.allocation); + + src_caps = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, enc->sbc.rate, + "channels", G_TYPE_INT, enc->sbc.channels, + "mode", G_TYPE_STRING, mode, + "subbands", G_TYPE_INT, enc->sbc.subbands, + "blocks", G_TYPE_INT, enc->sbc.blocks, + "allocation", G_TYPE_STRING, allocation, + NULL); return src_caps; } @@ -280,7 +284,7 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id, /* TODO - verify consistency */ enc->blocks = g_value_get_int(value); break; - case PROP_SUB_BANDS: + case PROP_SUBBANDS: /* TODO - verify consistency */ enc->subbands = g_value_get_int(value); break; @@ -305,7 +309,7 @@ static void gst_sbc_enc_get_property(GObject *object, guint prop_id, case PROP_BLOCKS: g_value_set_int(value, enc->blocks); break; - case PROP_SUB_BANDS: + case PROP_SUBBANDS: g_value_set_int(value, enc->subbands); break; default: @@ -341,7 +345,7 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) "Blocks", 0, G_MAXINT, SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); - g_object_class_install_property(object_class, PROP_SUB_BANDS, + g_object_class_install_property(object_class, PROP_SUBBANDS, g_param_spec_int("subbands", "Sub Bands", "Sub Bands", 0, G_MAXINT, SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE)); diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index e204870b..9f0c4d81 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -295,6 +295,7 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, } sbc_init(&parse->sbc, 0); break; + case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG("Finish subband codec"); if (parse->buffer) { @@ -303,6 +304,7 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, } sbc_finish(&parse->sbc); break; + default: break; } -- cgit From 347675ea994642c0ccd60f7af70f369f7a2d7a88 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 18 Oct 2007 23:02:24 +0000 Subject: Add rtp header. --- audio/gsta2dpsink.c | 55 +------------------------------------ audio/pcm_bluetooth.c | 55 +------------------------------------ audio/rtp.h | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 108 deletions(-) create mode 100644 audio/rtp.h (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 1bb88b36..433846eb 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -37,6 +37,7 @@ #include "ipc.h" #include "sbc.h" +#include "rtp.h" #include "gsta2dpsink.h" @@ -87,60 +88,6 @@ struct bluetooth_data { struct bluetooth_a2dp a2dp; /* A2DP data */ }; -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct rtp_header { - uint8_t cc:4; - uint8_t x:1; - uint8_t p:1; - uint8_t v:2; - - uint8_t pt:7; - uint8_t m:1; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - uint8_t frame_count:4; - uint8_t rfa0:1; - uint8_t is_last_fragment:1; - uint8_t is_first_fragment:1; - uint8_t is_fragmented:1; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct rtp_header { - uint8_t v:2; - uint8_t p:1; - uint8_t x:1; - uint8_t cc:4; - - uint8_t m:1; - uint8_t pt:7; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - uint8_t is_fragmented:1; - uint8_t is_first_fragment:1; - uint8_t is_last_fragment:1; - uint8_t rfa0:1; - uint8_t frame_count:4; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) #define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 806ed37b..ae1a1bcd 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -38,6 +38,7 @@ #include "ipc.h" #include "sbc.h" +#include "rtp.h" //#define ENABLE_DEBUG @@ -66,60 +67,6 @@ #define SCO_RXBUFS 0x04 #endif -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct rtp_header { - uint8_t cc:4; - uint8_t x:1; - uint8_t p:1; - uint8_t v:2; - - uint8_t pt:7; - uint8_t m:1; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - uint8_t frame_count:4; - uint8_t rfa0:1; - uint8_t is_last_fragment:1; - uint8_t is_first_fragment:1; - uint8_t is_fragmented:1; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct rtp_header { - uint8_t v:2; - uint8_t p:1; - uint8_t x:1; - uint8_t cc:4; - - uint8_t m:1; - uint8_t pt:7; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - uint8_t is_fragmented:1; - uint8_t is_first_fragment:1; - uint8_t is_last_fragment:1; - uint8_t rfa0:1; - uint8_t frame_count:4; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ diff --git a/audio/rtp.h b/audio/rtp.h new file mode 100644 index 00000000..931b7346 --- /dev/null +++ b/audio/rtp.h @@ -0,0 +1,76 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + uint8_t cc:4; + uint8_t x:1; + uint8_t p:1; + uint8_t v:2; + + uint8_t pt:7; + uint8_t m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t frame_count:4; + uint8_t rfa0:1; + uint8_t is_last_fragment:1; + uint8_t is_first_fragment:1; + uint8_t is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + uint8_t v:2; + uint8_t p:1; + uint8_t x:1; + uint8_t cc:4; + + uint8_t m:1; + uint8_t pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +struct rtp_payload { + uint8_t is_fragmented:1; + uint8_t is_first_fragment:1; + uint8_t is_last_fragment:1; + uint8_t rfa0:1; + uint8_t frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif -- cgit From f3ebb007ac66682bbba7926eac0c12258a945490 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 22 Oct 2007 09:01:09 +0000 Subject: Fix avdtp session leak when receiving consequtive start & suspend requests --- audio/a2dp.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index a91141d7..57192bd6 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -631,12 +631,13 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, else debug("SBC Source: Start_Ind"); - a2dp_sep->session = avdtp_ref(session); - - if (!a2dp_sep->locked) + if (!a2dp_sep->locked) { + a2dp_sep->session = avdtp_ref(session); a2dp_sep->suspend_timer = g_timeout_add(SUSPEND_TIMEOUT, (GSourceFunc) suspend_timeout, a2dp_sep); + } + return TRUE; } @@ -685,6 +686,8 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, if (a2dp_sep->suspend_timer) { g_source_remove(a2dp_sep->suspend_timer); a2dp_sep->suspend_timer = 0; + avdtp_unref(a2dp_sep->session); + a2dp_sep->session = NULL; } return TRUE; @@ -1184,6 +1187,8 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, if (sep->suspend_timer) { g_source_remove(sep->suspend_timer); sep->suspend_timer = 0; + avdtp_unref(sep->session); + sep->session = NULL; } g_idle_add((GSourceFunc) finalize_stream_setup, setup); } -- cgit 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/Makefile.am | 2 +- audio/control.c | 586 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ audio/control.h | 27 +++ audio/manager.c | 7 + 4 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 audio/control.c create mode 100644 audio/control.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 13ed4080..979adba3 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -13,7 +13,7 @@ service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ error.h error.c device.h device.c gateway.h gateway.c \ - sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h + sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ 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; +} + diff --git a/audio/control.h b/audio/control.h new file mode 100644 index 00000000..79d66ec7 --- /dev/null +++ b/audio/control.h @@ -0,0 +1,27 @@ +/* + * + * 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 + * + */ + +#define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" + +int control_init(DBusConnection *conn); +void control_exit(void); diff --git a/audio/manager.c b/audio/manager.c index f5a67cac..2b628544 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -59,6 +59,7 @@ #include "headset.h" #include "gateway.h" #include "sink.h" +#include "control.h" #include "manager.h" typedef enum { @@ -224,6 +225,9 @@ static gboolean server_is_enabled(uint16_t svc) break; case AUDIO_SINK_SVCLASS_ID: return enabled->sink; + case AV_REMOTE_TARGET_SVCLASS_ID: + case AV_REMOTE_SVCLASS_ID: + return enabled->control; default: ret = FALSE; break; @@ -1617,6 +1621,9 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, if (a2dp_init(conn, sources, sinks) < 0) goto failed; + if (enable->control && control_init(conn) < 0) + goto failed; + if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, manager_methods, -- 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') 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') 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') 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/avdtp.c | 21 +++++ audio/control.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/control.h | 11 ++- audio/device.c | 9 ++ audio/manager.c | 37 ++++++-- 5 files changed, 342 insertions(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 8e2094bf..222c1dea 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -43,6 +43,7 @@ #include "device.h" #include "manager.h" +#include "control.h" #include "avdtp.h" #include @@ -731,6 +732,13 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) static void connection_lost(struct avdtp *session, int err) { + struct device *dev; + + dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, + FALSE); + if (dev) + avrcp_disconnect(dev); + if (session->state == AVDTP_SESSION_STATE_CONNECTED) { char address[18]; @@ -1550,6 +1558,8 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, } if (session->state == AVDTP_SESSION_STATE_CONNECTING) { + struct device *dev; + session->mtu = l2o.imtu; session->buf = g_malloc0(session->mtu); session->state = AVDTP_SESSION_STATE_CONNECTED; @@ -1557,6 +1567,11 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); + + dev = manager_find_device(&session->dst, + AUDIO_CONTROL_INTERFACE, FALSE); + if (dev) + avrcp_connect(dev); } else if (session->pending_open) handle_transport_connect(session, sk, l2o.imtu); @@ -2682,6 +2697,7 @@ static void auth_cb(DBusPendingCall *call, void *data) struct avdtp *session = data; DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError err; + struct device *dev; dbus_pending_call_unref(session->pending_auth); session->pending_auth = NULL; @@ -2712,6 +2728,11 @@ static void auth_cb(DBusPendingCall *call, void *data) session->state = AVDTP_SESSION_STATE_CONNECTED; + dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, + FALSE); + if (dev) + avrcp_connect(dev); + g_source_remove(session->io); io = g_io_channel_unix_new(session->sock); 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; +} diff --git a/audio/control.h b/audio/control.h index 79d66ec7..aa183e02 100644 --- a/audio/control.h +++ b/audio/control.h @@ -23,5 +23,12 @@ #define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" -int control_init(DBusConnection *conn); -void control_exit(void); +int avrcp_init(DBusConnection *conn); +void avrcp_exit(void); + +gboolean avrcp_connect(struct device *dev); +void avrcp_disconnect(struct device *dev); + +struct control *control_init(struct device *dev); +void control_free(struct device *dev); +gboolean control_is_active(struct device *dev); diff --git a/audio/device.c b/audio/device.c index 9b76e019..a2047b9e 100644 --- a/audio/device.c +++ b/audio/device.c @@ -49,6 +49,7 @@ #include "ipc.h" #include "device.h" #include "avdtp.h" +#include "control.h" #include "headset.h" #include "sink.h" @@ -185,6 +186,9 @@ static void device_free(struct device *dev) if (dev->sink) sink_free(dev); + if (dev->control) + control_free(dev); + if (dev->conn) dbus_connection_unref(dev->conn); @@ -486,6 +490,8 @@ uint8_t device_get_state(struct device *dev) hs_state = headset_get_state(dev); return hs_to_ipc_state(hs_state); } + else if (dev->control && control_is_active(dev)) + return STATE_CONNECTED; return STATE_DISCONNECTED; } @@ -509,6 +515,9 @@ gboolean device_is_connected(struct device *dev, const char *interface) else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset && headset_is_active(dev)) return TRUE; + else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->headset && + control_is_active(dev)) + return TRUE; return FALSE; } diff --git a/audio/manager.c b/audio/manager.c index 2b628544..74588c78 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -279,9 +279,17 @@ static void handle_record(sdp_record_t *record, struct device *device) break; case AV_REMOTE_SVCLASS_ID: debug("Found AV Remote"); + if (device->control == NULL) + device->control = control_init(device); + if (device->sink && sink_is_active(device)) + avrcp_connect(device); break; case AV_REMOTE_TARGET_SVCLASS_ID: debug("Found AV Target"); + if (device->control == NULL) + device->control = control_init(device); + if (device->sink && sink_is_active(device)) + avrcp_connect(device); break; default: debug("Unrecognized UUID: 0x%04X", uuid16); @@ -639,8 +647,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) return NULL; headset = TRUE; - } - else if (!strcmp(uuid, A2DP_SOURCE_UUID)) { + } else if (!strcmp(uuid, A2DP_SOURCE_UUID)) { if (device->sink) return device; @@ -648,6 +655,14 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) if (!device->sink) return NULL; + } else if (!strcmp(uuid, AVRCP_TARGET_UUID)) { + if (device->control) + return device; + + device->control = control_init(device); + + if (!device->control) + return NULL; } else return NULL; @@ -1050,10 +1065,12 @@ static void parse_stored_devices(char *key, char *value, void *data) /* Change storage to source adapter */ bacpy(&device->store, src); - if (strstr(value, "headset")) + if (enabled->headset && strstr(value, "headset")) device->headset = headset_init(device, NULL, 0); - if (strstr(value, "sink")) + if (enabled->sink && strstr(value, "sink")) device->sink = sink_init(device); + if (enabled->control && strstr(value, "control")) + device->control = control_init(device); add_device(device, FALSE); } @@ -1621,7 +1638,7 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, if (a2dp_init(conn, sources, sinks) < 0) goto failed; - if (enable->control && control_init(conn) < 0) + if (enable->control && avrcp_init(conn) < 0) goto failed; if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, @@ -1761,15 +1778,19 @@ struct device *manager_find_device(bdaddr_t *bda, const char *interface, continue; if (interface && !strcmp(AUDIO_HEADSET_INTERFACE, interface) - && !dev->headset) + && !dev->headset) continue; if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface) - && !dev->sink) + && !dev->sink) continue; if (interface && !strcmp(AUDIO_SOURCE_INTERFACE, interface) - && !dev->source) + && !dev->source) + continue; + + if (interface && !strcmp(AUDIO_CONTROL_INTERFACE, interface) + && !dev->control) continue; if (connected && !device_is_connected(dev, interface)) -- 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') 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 cc791a620f77ab090ec5f7cb6977769465c83b80 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 23 Oct 2007 09:59:27 +0000 Subject: Fix unaligned access to ipc_data_cfg struct --- audio/unix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index c77f29bc..94330856 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -304,6 +304,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct ipc_codec_sbc *sbc = (void *) cfg->data; struct a2dp_data *a2dp = &client->d.a2dp; int fd; + uint16_t mtu; GSList *caps; client->req_id = 0; @@ -319,11 +320,13 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, a2dp->sep = sep; a2dp->stream = stream; - if (!avdtp_stream_get_transport(stream, &fd, &cfg->pkt_len, &caps)) { + if (!avdtp_stream_get_transport(stream, &fd, &mtu, &caps)) { error("Unable to get stream transport"); goto failed; } + cfg->pkt_len = mtu; + for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { cap = caps->data; if (cap->category == AVDTP_MEDIA_CODEC) { -- 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/a2dp.c | 1 + audio/a2dp.h | 1 + audio/avdtp.c | 1 + audio/avdtp.h | 2 +- audio/control.c | 5 +++-- audio/control.h | 5 +++-- audio/device.c | 1 + audio/device.h | 1 + audio/error.c | 1 + audio/error.h | 1 + audio/gateway.c | 1 + audio/gateway.h | 1 + audio/headset.c | 2 +- audio/headset.h | 1 + audio/main.c | 1 + audio/manager.c | 1 + audio/manager.h | 1 + audio/sink.c | 2 +- audio/sink.h | 1 + audio/unix.c | 2 +- audio/unix.h | 1 + 21 files changed, 25 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 57192bd6..83441617 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/a2dp.h b/audio/a2dp.h index dffdf259..6579f64c 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/avdtp.c b/audio/avdtp.c index 222c1dea..1ce76bc4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/avdtp.h b/audio/avdtp.h index a14355b9..99b95477 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * @@ -261,4 +262,3 @@ void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); int avdtp_init(void); void avdtp_exit(void); - 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 diff --git a/audio/control.h b/audio/control.h index aa183e02..42cd4826 100644 --- a/audio/control.h +++ b/audio/control.h @@ -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 diff --git a/audio/device.c b/audio/device.c index a2047b9e..f3567189 100644 --- a/audio/device.c +++ b/audio/device.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/device.h b/audio/device.h index 61ccc677..3fbf29de 100644 --- a/audio/device.h +++ b/audio/device.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/error.c b/audio/error.c index 3bec2f61..4e9a880b 100644 --- a/audio/error.c +++ b/audio/error.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/error.h b/audio/error.h index 712ee20d..377dea5b 100644 --- a/audio/error.h +++ b/audio/error.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/gateway.c b/audio/gateway.c index 66db457f..33cffa88 100644 --- a/audio/gateway.c +++ b/audio/gateway.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/gateway.h b/audio/gateway.h index 572457c9..c21e728b 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/headset.c b/audio/headset.c index f2bc1711..175ad861 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * @@ -1667,4 +1668,3 @@ int headset_get_sco_fd(struct device *dev) return g_io_channel_unix_get_fd(hs->sco); } - diff --git a/audio/headset.h b/audio/headset.h index 32bf701b..5e19cd9a 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/main.c b/audio/main.c index fb199a4b..2ec605d0 100644 --- a/audio/main.c +++ b/audio/main.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/manager.c b/audio/manager.c index 74588c78..6c51ddd2 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/manager.h b/audio/manager.h index e71e5d62..cbcf1672 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/sink.c b/audio/sink.c index 2515c968..e4fedb12 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * @@ -374,4 +375,3 @@ gboolean sink_new_stream(struct device *dev, struct avdtp *session, return TRUE; } - diff --git a/audio/sink.h b/audio/sink.h index 309379b1..a0cabdcd 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * diff --git a/audio/unix.c b/audio/unix.c index 94330856..eb849ddc 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * @@ -829,4 +830,3 @@ void unix_exit(void) close(unix_sock); unix_sock = -1; } - diff --git a/audio/unix.h b/audio/unix.h index feea855f..69fd3a4d 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * -- cgit From 6967924ef97d377c90d73d4b5127a9b3748cb068 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2007 14:08:31 +0000 Subject: Minor code cleanup --- audio/pcm_bluetooth.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index ae1a1bcd..2131e9ea 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -718,20 +718,21 @@ done: return ret; } -static int bluetooth_playback_delay(snd_pcm_ioplug_t *io, - snd_pcm_sframes_t *delayp) +static int bluetooth_playback_delay(snd_pcm_ioplug_t *io, + snd_pcm_sframes_t *delayp) { DBG(""); /* This updates io->hw_ptr value using pointer() function */ snd_pcm_hwsync(io->pcm); - + *delayp = io->appl_ptr - io->hw_ptr; if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) { io->callback->stop(io); io->state = SND_PCM_STATE_XRUN; *delayp = 0; } + /* This should never fail, ALSA API is really not prepared to handle a non zero return value */ return 0; -- cgit From f5befe8ab54b797a0a5357ceddf2f05ab0ff163c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2007 14:21:06 +0000 Subject: Revert buffer contraints patch (it wasn't doing what was intended and it actually caused errors for applications in some cases) --- audio/pcm_bluetooth.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2131e9ea..6f9bbe0b 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -46,8 +46,7 @@ #define MIN_PERIOD_TIME 1 -#define MIN_BUFFER_SIZE 256 /* minimum size of buffer */ -#define MAX_BUFFER_SIZE 16384 /* allocated RAM for buffer */ +#define BUFFER_SIZE 2048 #ifdef ENABLE_DEBUG #define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) @@ -71,7 +70,7 @@ struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ int samples; /* Number of encoded samples */ - uint8_t buffer[MAX_BUFFER_SIZE];/* Codec transfer buffer */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ int count; /* Codec transfer buffer counter */ int nsamples; /* Cumulative number of codec samples */ @@ -85,7 +84,7 @@ struct bluetooth_data { struct ipc_data_cfg cfg; /* Bluetooth device config */ struct pollfd stream; /* Audio stream filedescriptor */ struct pollfd server; /* Audio daemon filedescriptor */ - uint8_t buffer[MAX_BUFFER_SIZE];/* Encoded transfer buffer */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ int count; /* Transfer buffer counter */ struct bluetooth_a2dp a2dp; /* A2DP data */ @@ -893,22 +892,12 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, a2dp->codesize, - MAX_BUFFER_SIZE / 2); + a2dp->codesize); if (err < 0) return err; - /* supported buffer sizes */ - err = snd_pcm_ioplug_set_param_minmax(io, - SND_PCM_IOPLUG_HW_BUFFER_BYTES, - MIN_BUFFER_SIZE, - MAX_BUFFER_SIZE); - if (err < 0) - return err; - - /* supported period count: - * - derived from max buffer size and minimum period size */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, MAX_BUFFER_SIZE / a2dp->codesize); + 2, 50); if (err < 0) return err; -- 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') 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 999314c0bc7a87f1fb4a771569aaed99aa9ac5b6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2007 20:02:53 +0000 Subject: Add support for Enable config option --- audio/main.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 2ec605d0..61ca1e3e 100644 --- a/audio/main.c +++ b/audio/main.c @@ -93,6 +93,27 @@ static void read_config(const char *file) g_free(str); } + str = g_key_file_get_string(keyfile, "General", "Enable", &err); + if (err) { + debug("%s: %s", file, err->message); + g_error_free(err); + err = NULL; + } else { + if (strstr(str, "Headset")) + enabled.headset = TRUE; + if (strstr(str, "Gateway")) + enabled.gateway = TRUE; + if (strstr(str, "Sink")) + enabled.sink = TRUE; + if (strstr(str, "Source")) + enabled.source = TRUE; + if (strstr(str, "Control")) + enabled.control = TRUE; + if (strstr(str, "Target")) + enabled.target = TRUE; + g_free(str); + } + str = g_key_file_get_string(keyfile, "General", "Disable", &err); if (err) { debug("%s: %s", file, err->message); -- cgit From d05be53901f38d2e64c63283e4837b20f8e916b6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 24 Oct 2007 21:13:12 +0000 Subject: Fix a2dpsink coding style problems and improve compatibility with some players. --- audio/gsta2dpsink.c | 275 ++++++++++++++++++++++++++++------------------------ audio/gsta2dpsink.h | 2 +- audio/manager.c | 3 +- 3 files changed, 149 insertions(+), 131 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 433846eb..c5761148 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -70,23 +70,18 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); g_cond_signal (s->con_conf_end); \ } G_STMT_END -struct bluetooth_a2dp { +struct bluetooth_data { + struct ipc_data_cfg cfg; /* Bluetooth device config */ sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ int samples; /* Number of encoded samples */ - uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - int count; /* Codec transfer buffer counter */ + gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + gsize count; /* Codec transfer buffer counter */ int nsamples; /* Cumulative number of codec samples */ uint16_t seq_num; /* Cumulative packet sequence */ int frame_count; /* Current frames in buffer*/ }; -struct bluetooth_data { - struct ipc_data_cfg cfg; /* Bluetooth device config */ - uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ - int count; /* Transfer buffer counter */ - struct bluetooth_a2dp a2dp; /* A2DP data */ -}; #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) #define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) @@ -132,15 +127,13 @@ static void gst_a2dp_sink_base_init(gpointer g_class) static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) { GstA2dpSink *self = GST_A2DP_SINK(basesink); - struct bluetooth_a2dp *a2dp = &self->data->a2dp; self->con_state = NOT_CONFIGURED; self->total = 0; - if (self->stream) { - g_io_channel_close(self->stream); - g_io_channel_unref(self->stream); - self->stream = NULL; + if (self->watch_id != 0) { + g_source_remove(self->watch_id); + self->watch_id = 0; } if (self->server) { @@ -149,10 +142,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) self->stream = NULL; } - if (self->data->cfg.codec == CFG_CODEC_SBC) - sbc_finish(&a2dp->sbc); - if (self->data) { + if (self->data->cfg.codec == CFG_CODEC_SBC) + sbc_finish(&self->data->sbc); g_free(self->data); self->data = NULL; } @@ -244,6 +236,7 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) && cmsg->cmsg_type == SCM_RIGHTS) { stream_fd = (*(int *) CMSG_DATA(cmsg)); sink->stream = g_io_channel_unix_new(stream_fd); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd); return 0; } @@ -252,14 +245,14 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) return -EINVAL; } -static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, +static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, struct ipc_codec_sbc *sbc) { - struct bluetooth_a2dp *a2dp = &sink->data->a2dp; - struct ipc_data_cfg *cfg = &sink->data->cfg; + struct bluetooth_data *data = self->data; + struct ipc_data_cfg *cfg = &data->cfg; if (cfg == NULL) { - GST_ERROR_OBJECT(sink, "Error getting codec parameters"); + GST_ERROR_OBJECT(self, "Error getting codec parameters"); return -1; } @@ -267,24 +260,24 @@ static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *sink, return -1; /* FIXME: init using flags? */ - sbc_init(&a2dp->sbc, 0); - a2dp->sbc.rate = cfg->rate; - a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; + sbc_init(&data->sbc, 0); + data->sbc.rate = cfg->rate; + data->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) - a2dp->sbc.joint = 1; - a2dp->sbc.allocation = sbc->allocation; - a2dp->sbc.subbands = sbc->subbands; - a2dp->sbc.blocks = sbc->blocks; - a2dp->sbc.bitpool = sbc->bitpool; - a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - - GST_DEBUG_OBJECT(sink, "Codec parameters: \ - \tallocation=%u\n\tsubbands=%u\n \ - \tblocks=%u\n\tbitpool=%u\n", - a2dp->sbc.allocation, a2dp->sbc.subbands, - a2dp->sbc.blocks, a2dp->sbc.bitpool); + data->sbc.joint = 1; + data->sbc.allocation = sbc->allocation; + data->sbc.subbands = sbc->subbands; + data->sbc.blocks = sbc->blocks; + data->sbc.bitpool = sbc->bitpool; + data->codesize = data->sbc.subbands * data->sbc.blocks * + data->sbc.channels * 2; + data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + + GST_DEBUG_OBJECT(self, "Codec parameters: " + "\tallocation=%u\n\tsubbands=%u\n " + "\tblocks=%u\n\tbitpool=%u\n", + data->sbc.allocation, data->sbc.subbands, + data->sbc.blocks, data->sbc.bitpool); return 0; } @@ -373,21 +366,21 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) io_error = g_io_channel_read(sink->server, (gchar *) buf, sizeof(*pkt) + sizeof(*cfg), &ret); if (io_error != G_IO_ERROR_NONE && ret > 0) { - GST_ERROR_OBJECT(sink, "Error ocurred while receiving \ - configurarion packet answer"); + GST_ERROR_OBJECT(sink, "Error ocurred while receiving " + "configurarion packet answer"); return FALSE; } sink->total = ret; if (pkt->type != PKT_TYPE_CFG_RSP) { - GST_ERROR_OBJECT(sink, "Unexpected packet type %d \ - received", pkt->type); + GST_ERROR_OBJECT(sink, "Unexpected packet type %d " + "received", pkt->type); return FALSE; } if (pkt->error != PKT_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error %d while configuring \ - device", pkt->error); + GST_ERROR_OBJECT(sink, "Error %d while configuring " + "device", pkt->error); return FALSE; } @@ -411,8 +404,8 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) io_error = g_io_channel_read(sink->server, (gchar *) sbc, sizeof(*sbc), &ret); if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error while reading data from socket \ - %s (%d)", strerror(errno), errno); + GST_ERROR_OBJECT(sink, "Error while reading data from socket " + "%s (%d)", strerror(errno), errno); return FALSE; } else if (ret == 0) { GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); @@ -421,19 +414,19 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) sink->total += ret; GST_DEBUG_OBJECT(sink, "OK - %d bytes received", sink->total); - +#if 0 if (pkt->length != (sink->total - sizeof(struct ipc_packet))) { GST_ERROR_OBJECT(sink, "Error while configuring device: " - "packet size doesn't match"); + "packet size doesn't match"); return FALSE; } - +#endif memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); - GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t\ - fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - sink->stream, sink->data->cfg.fd_opt, \ - sink->data->cfg.pkt_len, sink->data->cfg.sample_size, \ + GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" + "fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", + sink->stream, sink->data->cfg.fd_opt, + sink->data->cfg.pkt_len, sink->data->cfg.sample_size, sink->data->cfg.rate); if (sink->data->cfg.codec == CFG_CODEC_SBC) { @@ -445,34 +438,70 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) return TRUE; } -static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *sink) +static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) { + struct bluetooth_data *data = self->data; gint ret; GIOError err; - gsize read; + GError *gerr = NULL; + GIOStatus status; + GIOFlags flags; + gsize read ; - ret = gst_a2dp_sink_bluetooth_recvmsg_fd(sink); + ret = gst_a2dp_sink_bluetooth_recvmsg_fd(self); if (ret < 0) return FALSE; - if (!sink->stream) { - GST_ERROR_OBJECT(sink, "Error while configuring device: " + if (!self->stream) { + GST_ERROR_OBJECT(self, "Error while configuring device: " "could not acquire audio socket"); return FALSE; } + /* set stream socket to nonblock */ + GST_LOG_OBJECT(self, "setting stream socket to nonblock"); + flags = g_io_channel_get_flags(self->stream); + flags |= G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to nonblock: " + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to nonblock"); + } + /* It is possible there is some outstanding data in the pipe - we have to empty it */ + GST_LOG_OBJECT(self, "emptying stream pipe"); while (1) { - err = g_io_channel_read(sink->stream, - (gchar *) sink->data->buffer, - (gsize) sink->data->cfg.pkt_len, + err = g_io_channel_read(self->stream, data->buffer, + (gsize) data->cfg.pkt_len, &read); if (err != G_IO_ERROR_NONE || read <= 0) break; } - memset(sink->data->buffer, 0, sizeof(sink->data->buffer)); + /* set stream socket to block */ + GST_LOG_OBJECT(self, "setting stream socket to block"); + flags = g_io_channel_get_flags(self->stream); + flags &= ~G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to block:" + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to block"); + } + + memset(data->buffer, 0, sizeof(data->buffer)); return TRUE; } @@ -512,28 +541,23 @@ static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { - GstA2dpSink *sink = GST_A2DP_SINK(data); + GstA2dpSink *sink; - switch (cond) { - case G_IO_IN: + if (cond & G_IO_HUP || cond & G_IO_NVAL) + return FALSE; + else if (cond & G_IO_ERR) { + sink = GST_A2DP_SINK(data); + GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); + } else if (cond & G_IO_IN) { + sink = GST_A2DP_SINK(data); if (sink->con_state != NOT_CONFIGURED && sink->con_state != CONFIGURED) gst_a2dp_sink_conf_recv_data(sink); else GST_WARNING_OBJECT(sink, "Unexpected data received"); - break; - case G_IO_HUP: - return FALSE; - break; - case G_IO_ERR: - GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - break; - case G_IO_NVAL: - return FALSE; - break; - default: + } else { + sink = GST_A2DP_SINK(data); GST_WARNING_OBJECT(sink, "Unexpected callback call"); - break; } return TRUE; @@ -546,6 +570,8 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) gint sk; gint err; + self->watch_id = 0; + sk = socket(PF_LOCAL, SOCK_STREAM, 0); if (sk < 0) { err = errno; @@ -556,7 +582,7 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { err = errno; - GST_ERROR_OBJECT(self, "Connection fail %s (%d)", + GST_ERROR_OBJECT(self, "Connection fail %s (%d)", strerror(err), err); close(sk); return FALSE; @@ -564,11 +590,16 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) self->server = g_io_channel_unix_new(sk); - g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - server_callback, self); + self->watch_id = g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | + G_IO_ERR | G_IO_NVAL, server_callback, self); self->data = g_new0(struct bluetooth_data, 1); - memset(self->data, 0, sizeof(struct bluetooth_data)); + + self->stream = NULL; + self->con_state = NOT_CONFIGURED; + self->total = 0; + + self->waiting_con_conf = FALSE; return TRUE; } @@ -586,8 +617,8 @@ static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) memset (pkt, 0, sizeof(buf)); ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); if (!ret) { - GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps \ - to packet configuration"); + GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps " + "to packet configuration"); return FALSE; } @@ -596,8 +627,8 @@ static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) io_error = g_io_channel_write(sink->server, (gchar *) pkt, sizeof(*pkt) + pkt->length, &bytes_sent); if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error ocurred while sending \ - configurarion packet"); + GST_ERROR_OBJECT(sink, "Error ocurred while sending " + "configurarion packet"); sink->con_state = NOT_CONFIGURED; return FALSE; } @@ -643,47 +674,37 @@ static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, return GST_FLOW_OK; } -static int gst_a2dp_sink_avdtp_write(GstA2dpSink *sink) +static int gst_a2dp_sink_avdtp_write(GstA2dpSink *self) { - int ret = 0; - struct bluetooth_data *data; + gsize ret; + struct bluetooth_data *data = self->data; struct rtp_header *header; struct rtp_payload *payload; - struct bluetooth_a2dp *a2dp; GIOError err; - data = sink->data; - a2dp = &data->a2dp; - - header = (void *) a2dp->buffer; - payload = (void *) (a2dp->buffer + sizeof(*header)); + header = (void *) data->buffer; + payload = (void *) (data->buffer + sizeof(*header)); - memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); + memset(data->buffer, 0, sizeof(*header) + sizeof(*payload)); - payload->frame_count = a2dp->frame_count; + payload->frame_count = data->frame_count; header->v = 2; header->pt = 1; - header->sequence_number = htons(a2dp->seq_num); - header->timestamp = htonl(a2dp->nsamples); + header->sequence_number = htons(data->seq_num); + header->timestamp = htonl(data->nsamples); header->ssrc = htonl(1); - while (1) { - err = g_io_channel_write(sink->stream, (const char *) a2dp->buffer, - (gsize) a2dp->count, (gsize *) &ret); - - if (err == G_IO_ERROR_AGAIN) { - usleep (100); - continue; - } - - break; + err = g_io_channel_write(self->stream, data->buffer, data->count, &ret); + if (err != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while sending data"); + ret = -1; } /* Reset buffer of data to send */ - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - a2dp->frame_count = 0; - a2dp->samples = 0; - a2dp->seq_num++; + data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + data->frame_count = 0; + data->samples = 0; + data->seq_num++; return ret; } @@ -691,29 +712,22 @@ static int gst_a2dp_sink_avdtp_write(GstA2dpSink *sink) static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, GstBuffer *buffer) { - GstA2dpSink *sink; - struct bluetooth_data *data; - struct bluetooth_a2dp *a2dp; - gint encoded, frame_size=1024; - gint ret=0; - - sink = GST_A2DP_SINK(basesink); - data = (struct bluetooth_data*) sink->data; - a2dp = &data->a2dp; + GstA2dpSink *self = GST_A2DP_SINK(basesink); + struct bluetooth_data *data = self->data; + gint encoded; + gint ret; encoded = GST_BUFFER_SIZE(buffer); - if (a2dp->count + encoded >= data->cfg.pkt_len) { - ret = gst_a2dp_sink_avdtp_write(sink); + if (data->count + encoded >= data->cfg.pkt_len) { + ret = gst_a2dp_sink_avdtp_write(self); if (ret < 0) return GST_FLOW_ERROR; } - memcpy(a2dp->buffer + a2dp->count, GST_BUFFER_DATA(buffer), encoded); - a2dp->count += encoded; - a2dp->frame_count++; - a2dp->samples += encoded / frame_size; - a2dp->nsamples += encoded / frame_size; + memcpy(data->buffer + data->count, GST_BUFFER_DATA(buffer), encoded); + data->count += encoded; + data->frame_count++; return GST_FLOW_OK; } @@ -732,6 +746,11 @@ static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) { + GstA2dpSink *self = GST_A2DP_SINK(basesink); + + if (self->stream != NULL) + g_io_channel_flush (self->stream, NULL); + return TRUE; } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 6c7cfe2a..c9bcabd1 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -67,7 +67,7 @@ struct _GstA2dpSink { GMutex *sink_lock; gint total; - + guint watch_id; }; struct _GstA2dpSinkClass { diff --git a/audio/manager.c b/audio/manager.c index 6c51ddd2..2ed9f39e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1100,10 +1100,9 @@ static void register_devices_stored(const char *adapter) bacpy(&default_src, BDADDR_ANY); dev_id = hci_get_route(&default_src); - if (dev_id < 0) + if (dev_id < 0 || hci_devba(dev_id, &default_src)) return; - hci_devba(dev_id, &default_src); if (bacmp(&default_src, &src) != 0) return; -- cgit From ac568573b26dc0d7f1a92457296606205dc144b1 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 24 Oct 2007 21:22:40 +0000 Subject: Fix hci_devba check. --- audio/manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 2ed9f39e..6c23923f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1100,7 +1100,7 @@ static void register_devices_stored(const char *adapter) bacpy(&default_src, BDADDR_ANY); dev_id = hci_get_route(&default_src); - if (dev_id < 0 || hci_devba(dev_id, &default_src)) + if (dev_id < 0 || hci_devba(dev_id, &default_src) < 0) return; if (bacmp(&default_src, &src) != 0) -- cgit From c522cb3f23849475348aa9c4ae6a70c83a1c2675 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 24 Oct 2007 21:33:18 +0000 Subject: Fix coding style issues --- audio/gstsbcutil.c | 18 +++++++----------- audio/gstsbcutil.h | 15 ++++++--------- 2 files changed, 13 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 706e7f54..783a2d92 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -21,12 +21,8 @@ * */ - -/* common functions for gstreamer sbc related plugins */ - - -#include "gstsbcutil.h" #include "ipc.h" +#include "gstsbcutil.h" /* * Selects one rate from a list of possible rates @@ -108,7 +104,7 @@ gint gst_sbc_select_subbands_from_range(const GValue *value) * Selects one allocation mode from the ones on the list * TODO - use a better approach */ -const gchar* gst_sbc_get_allocation_from_list(const GValue *value) +const gchar *gst_sbc_get_allocation_from_list(const GValue *value) { guint size = gst_value_list_get_size(value); return g_value_get_string(gst_value_list_get_value(value, size-1)); @@ -118,13 +114,13 @@ const gchar* gst_sbc_get_allocation_from_list(const GValue *value) * Selects one mode from the ones on the list * TODO - use a better aproach */ -const gchar* gst_sbc_get_mode_from_list(const GValue *value) +const gchar *gst_sbc_get_mode_from_list(const GValue *value) { guint size = gst_value_list_get_size(value); return g_value_get_string(gst_value_list_get_value(value, size-1)); } -gint gst_sbc_get_allocation_mode_int(const gchar* allocation) +gint gst_sbc_get_allocation_mode_int(const gchar *allocation) { if (g_ascii_strcasecmp(allocation, "loudness") == 0) return CFG_ALLOCATION_LOUDNESS; @@ -136,7 +132,7 @@ gint gst_sbc_get_allocation_mode_int(const gchar* allocation) return -1; } -gint gst_sbc_get_mode_int(const gchar* mode) +gint gst_sbc_get_mode_int(const gchar *mode) { if (g_ascii_strcasecmp(mode, "joint") == 0) return CFG_MODE_JOINT_STEREO; @@ -152,7 +148,7 @@ gint gst_sbc_get_mode_int(const gchar* mode) return -1; } -const gchar* gst_sbc_get_mode_string(int joint) +const gchar *gst_sbc_get_mode_string(int joint) { switch (joint) { case CFG_MODE_MONO: @@ -170,7 +166,7 @@ const gchar* gst_sbc_get_mode_string(int joint) } } -const gchar* gst_sbc_get_allocation_string(int alloc) +const gchar *gst_sbc_get_allocation_string(int alloc) { switch (alloc) { case CFG_ALLOCATION_LOUDNESS: diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 84161d85..70169a74 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -21,9 +21,6 @@ * */ - -/* common functions for gstreamer sbc related plugins */ - #include gint gst_sbc_select_rate_from_list(const GValue *value); @@ -38,10 +35,10 @@ gint gst_sbc_select_blocks_from_range(const GValue *value); gint gst_sbc_select_subbands_from_list(const GValue *value); gint gst_sbc_select_subbands_from_range(const GValue *value); -const gchar* gst_sbc_get_allocation_from_list(const GValue *value); -gint gst_sbc_get_allocation_mode_int(const gchar* allocation); -const gchar* gst_sbc_get_allocation_string(int alloc); +const gchar *gst_sbc_get_allocation_from_list(const GValue *value); +gint gst_sbc_get_allocation_mode_int(const gchar *allocation); +const gchar *gst_sbc_get_allocation_string(int alloc); -const gchar* gst_sbc_get_mode_from_list(const GValue *value); -gint gst_sbc_get_mode_int(const gchar* mode); -const gchar* gst_sbc_get_mode_string(int joint); +const gchar *gst_sbc_get_mode_from_list(const GValue *value); +gint gst_sbc_get_mode_int(const gchar *mode); +const gchar *gst_sbc_get_mode_string(int joint); -- cgit From e65e4ac6f2c8a4a5b5276f2ad23d20bf7feb44de Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 24 Oct 2007 21:33:22 +0000 Subject: Fix coding style. --- audio/gsta2dpsink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index c5761148..6456c8f6 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -357,7 +357,7 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) { gchar buf[IPC_MTU]; GIOError io_error; - guint ret; + gsize ret; struct ipc_packet *pkt = (void *) buf; struct ipc_data_cfg *cfg = (void *) pkt->data; @@ -396,7 +396,7 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) { gchar buf[IPC_MTU]; GIOError io_error; - guint ret=0; + gsize ret; struct ipc_packet *pkt = (void *) buf; struct ipc_data_cfg *cfg = (void *) pkt->data; struct ipc_codec_sbc *sbc = (void *) cfg->data; @@ -446,7 +446,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) GError *gerr = NULL; GIOStatus status; GIOFlags flags; - gsize read ; + gsize read; ret = gst_a2dp_sink_bluetooth_recvmsg_fd(self); if (ret < 0) @@ -609,7 +609,7 @@ static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) gchar buf[IPC_MTU]; struct ipc_packet *pkt = (void *) buf; gboolean ret; - guint bytes_sent; + gsize bytes_sent; GIOError io_error; g_assert(sink->con_state == NOT_CONFIGURED); -- cgit From 3de478447168a2520e1c69f59abe47353cefa065 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 24 Oct 2007 21:36:29 +0000 Subject: Don't forget to include the config options --- audio/gstsbcutil.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'audio') diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 783a2d92..c795aedc 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -21,6 +21,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include "ipc.h" #include "gstsbcutil.h" -- cgit From 040de64c864639dee09801f3a9cdb32599819da5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 24 Oct 2007 21:40:35 +0000 Subject: Some more coding style fixes --- audio/gsta2dpsink.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 6456c8f6..2066496f 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -350,7 +350,6 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, pkt->error = PKT_ERROR_NONE; return TRUE; - } static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) @@ -433,8 +432,8 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); if (ret < 0) return FALSE; - } + return TRUE; } @@ -499,7 +498,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) GST_WARNING_OBJECT(self, "Error while " "setting server " "socket to block"); - } + } memset(data->buffer, 0, sizeof(data->buffer)); -- cgit From dda179a302219fd1b48711edb7d388a331ab3e2e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 24 Oct 2007 21:51:37 +0000 Subject: Some more coding style changes --- audio/gstsbcparse.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 9f0c4d81..a15a75fb 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -55,10 +55,7 @@ static GstStaticPadTemplate sbc_parse_src_factory = "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }")); -/* - Creates a fixed caps from the caps given. - -*/ +/* reates a fixed caps from the caps given. */ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) { GstCaps *result; @@ -72,7 +69,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) structure = gst_caps_get_structure(caps, 0); - /* rate */ if (!gst_structure_has_field(structure, "rate")) { error = TRUE; error_message = "no rate."; @@ -89,7 +85,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) rate = temp; } - /* channels */ if (!gst_structure_has_field(structure, "channels")) { error = TRUE; error_message = "no channels."; @@ -106,7 +101,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) channels = temp; } - /* blocks */ if (!gst_structure_has_field(structure, "blocks")) { error = TRUE; error_message = "no blocks."; @@ -123,7 +117,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) blocks = temp; } - /* subbands */ if (!gst_structure_has_field(structure, "subbands")) { error = TRUE; error_message = "no subbands."; @@ -140,7 +133,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) subbands = temp; } - /* allocation */ if (!gst_structure_has_field(structure, "allocation")) { error = TRUE; error_message = "no allocation."; @@ -154,7 +146,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) } } - /* mode */ if (!gst_structure_has_field(structure, "mode")) { error = TRUE; error_message = "no mode."; @@ -175,7 +166,6 @@ error: return NULL; } - result = gst_caps_new_simple("audio/x-sbc", "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, @@ -250,14 +240,12 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) if (consumed <= 0) break; - temp = GST_PAD_CAPS(parse->srcpad); res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad, GST_BUFFER_OFFSET_NONE, consumed, temp, &output); - if (res != GST_FLOW_OK) goto done; -- cgit From 8c29a4d5d2635d663657d731a80a9c0f6e0d7216 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 25 Oct 2007 10:33:03 +0000 Subject: Add rtp.h file to build dependencies --- audio/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 979adba3..48c2a80f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -23,7 +23,7 @@ alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la -libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h +libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h rtp.h libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ @@ -39,7 +39,7 @@ gstreamerdir = $(libdir)/gstreamer-0.10 gstreamer_LTLIBRARIES = libgstbluetooth.la -libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h \ +libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h rtp.h \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ -- 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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- audio/device.c | 62 +++++++++++++---------- audio/device.h | 1 + 3 files changed, 186 insertions(+), 32 deletions(-) (limited to 'audio') 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)); diff --git a/audio/device.c b/audio/device.c index f3567189..c86f711d 100644 --- a/audio/device.c +++ b/audio/device.c @@ -73,51 +73,60 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, return send_message_and_unref(conn, reply); } -static DBusHandlerResult device_get_name(DBusConnection *conn, - DBusMessage *msg, void *data) +static char *get_dev_name(DBusConnection *conn, const char *adapter_path, + bdaddr_t *bda) { - struct device *device = data; - DBusMessage *reply, *reply2, *msg2; + DBusMessage *msg, *reply; DBusError derr; const char *name; - char address[18], *addr_ptr = address; + char address[18], *addr_ptr = address, *ret; - msg2 = dbus_message_new_method_call("org.bluez", device->adapter_path, + msg = dbus_message_new_method_call("org.bluez", adapter_path, "org.bluez.Adapter", "GetRemoteName"); - if (!msg2) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + if (!msg) + return NULL; - ba2str(&device->dst, address); - dbus_message_append_args(msg2, DBUS_TYPE_STRING, &addr_ptr, + ba2str(bda, address); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_INVALID); dbus_error_init(&derr); - reply2 = dbus_connection_send_with_reply_and_block(conn, msg2, -1, + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &derr); - - dbus_message_unref(msg2); + dbus_message_unref(msg); if (dbus_error_is_set(&derr)) { - error("%s GetRemoteName(): %s", device->adapter_path, - derr.message); + error("%s GetRemoteName(): %s", adapter_path, derr.message); dbus_error_free(&derr); - return err_failed(conn, msg, "Unable to get remote name"); + return NULL; } + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + return NULL; + + ret = g_strdup(name); + + dbus_message_unref(reply); + + return ret; +} + +static DBusHandlerResult device_get_name(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct device *dev = data; + DBusMessage *reply; + const char *name = dev->name ? dev->name : ""; reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - dbus_message_get_args(reply2, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); - dbus_message_unref(reply2); - return send_message_and_unref(conn, reply); } @@ -193,11 +202,9 @@ static void device_free(struct device *dev) if (dev->conn) dbus_connection_unref(dev->conn); - if (dev->adapter_path) - g_free(dev->adapter_path); - - if (dev->path) - g_free(dev->path); + g_free(dev->adapter_path); + g_free(dev->path); + g_free(dev->name); g_free(dev); } @@ -303,6 +310,7 @@ struct device *device_register(DBusConnection *conn, return NULL; } + dev->name = get_dev_name(conn, dev->adapter_path, bda); dev->path = g_strdup(path); bacpy(&dev->dst, bda); bacpy(&dev->src, &src); diff --git a/audio/device.h b/audio/device.h index 3fbf29de..04ce36de 100644 --- a/audio/device.h +++ b/audio/device.h @@ -56,6 +56,7 @@ struct device { DBusConnection *conn; char *adapter_path; char *path; + char *name; bdaddr_t store; bdaddr_t src; bdaddr_t dst; -- 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/avdtp.c | 8 +++----- audio/control.c | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 1ce76bc4..00045820 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -561,6 +561,9 @@ static void stream_free(struct avdtp_stream *stream) if (stream->timer) g_source_remove(stream->timer); + if (stream->sock >= 0) + close(stream->sock); + if (stream->io) g_source_remove(stream->io); @@ -720,11 +723,6 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) { struct avdtp_local_sep *sep = stream->lsep; - if (stream->sock >= 0) - close(stream->sock); - if (stream->io) - g_source_remove(stream->io); - if (sep->cfm && sep->cfm->abort) sep->cfm->abort(session, sep, stream, NULL, sep->user_data); 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 6a7bdf306c26ac5663ecd9f1637839a7dd0141d4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 25 Oct 2007 21:07:50 +0000 Subject: Fix buffer usage. --- audio/gsta2dpsink.c | 49 ++++++++++++++++++++++--------------------------- audio/gsta2dpsink.h | 10 ---------- 2 files changed, 22 insertions(+), 37 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 2066496f..a80fd790 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -41,6 +41,14 @@ #include "gsta2dpsink.h" +enum { + NOT_CONFIGURED, + CONFIGURING_INIT, + CONFIGURING_SENT_CONF, + CONFIGURING_RCVD_DEV_CONF, + CONFIGURED +}; + GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug @@ -128,8 +136,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) { GstA2dpSink *self = GST_A2DP_SINK(basesink); + GST_INFO_OBJECT(self, "stop"); + self->con_state = NOT_CONFIGURED; - self->total = 0; if (self->watch_id != 0) { g_source_remove(self->watch_id); @@ -357,8 +366,10 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) gchar buf[IPC_MTU]; GIOError io_error; gsize ret; + gint total; struct ipc_packet *pkt = (void *) buf; struct ipc_data_cfg *cfg = (void *) pkt->data; + struct ipc_codec_sbc *sbc = (void *) cfg->data; memset(buf, 0, sizeof(buf)); @@ -370,7 +381,7 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) return FALSE; } - sink->total = ret; + total = ret; if (pkt->type != PKT_TYPE_CFG_RSP) { GST_ERROR_OBJECT(sink, "Unexpected packet type %d " "received", pkt->type); @@ -388,18 +399,6 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) return FALSE; } - return TRUE; -} - -static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) -{ - gchar buf[IPC_MTU]; - GIOError io_error; - gsize ret; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; - io_error = g_io_channel_read(sink->server, (gchar *) sbc, sizeof(*sbc), &ret); if (io_error != G_IO_ERROR_NONE) { @@ -411,15 +410,15 @@ static gboolean gst_a2dp_sink_conf_recv_dev_conf(GstA2dpSink *sink) return FALSE; } - sink->total += ret; - GST_DEBUG_OBJECT(sink, "OK - %d bytes received", sink->total); -#if 0 - if (pkt->length != (sink->total - sizeof(struct ipc_packet))) { + total += ret; + GST_DEBUG_OBJECT(sink, "OK - %d bytes received", total); + + if (pkt->length != (total - sizeof(struct ipc_packet))) { GST_ERROR_OBJECT(sink, "Error while configuring device: " "packet size doesn't match"); return FALSE; } -#endif + memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" @@ -516,15 +515,10 @@ static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) switch (sink->con_state) { case CONFIGURING_SENT_CONF: if (gst_a2dp_sink_conf_resp(sink)) - sink->con_state = CONFIGURING_RCVD_CONF_RSP; - else - GST_A2DP_SINK_CONFIGURATION_FAIL(sink); - break; - case CONFIGURING_RCVD_CONF_RSP: - if (gst_a2dp_sink_conf_recv_dev_conf(sink)) sink->con_state = CONFIGURING_RCVD_DEV_CONF; else GST_A2DP_SINK_CONFIGURATION_FAIL(sink); + break; case CONFIGURING_RCVD_DEV_CONF: if (gst_a2dp_sink_conf_recv_stream_fd(sink)) GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); @@ -569,6 +563,8 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) gint sk; gint err; + GST_INFO_OBJECT(self, "start"); + self->watch_id = 0; sk = socket(PF_LOCAL, SOCK_STREAM, 0); @@ -593,10 +589,10 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) G_IO_ERR | G_IO_NVAL, server_callback, self); self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); self->stream = NULL; self->con_state = NOT_CONFIGURED; - self->total = 0; self->waiting_con_conf = FALSE; @@ -789,7 +785,6 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) self->stream = NULL; self->con_state = NOT_CONFIGURED; - self->total = 0; self->con_conf_end = g_cond_new(); self->waiting_con_conf = FALSE; diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index c9bcabd1..76d512b9 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -37,15 +37,6 @@ G_BEGIN_DECLS #define GST_IS_A2DP_SINK_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) -enum { - NOT_CONFIGURED, - CONFIGURING_INIT, - CONFIGURING_SENT_CONF, - CONFIGURING_RCVD_CONF_RSP, - CONFIGURING_RCVD_DEV_CONF, - CONFIGURED -}; - typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; @@ -66,7 +57,6 @@ struct _GstA2dpSink { gboolean waiting_con_conf; GMutex *sink_lock; - gint total; guint watch_id; }; -- cgit From 973d90d520edc392c458c556d9f1a3e242a1f080 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 26 Oct 2007 18:48:42 +0000 Subject: Remove dependency of sbc library from a2dpsink element. --- audio/gsta2dpsink.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index a80fd790..0ed39a66 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -36,7 +36,6 @@ #include #include "ipc.h" -#include "sbc.h" #include "rtp.h" #include "gsta2dpsink.h" @@ -80,8 +79,6 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); struct bluetooth_data { struct ipc_data_cfg cfg; /* Bluetooth device config */ - sbc_t sbc; /* Codec data */ - int codesize; /* SBC codesize */ int samples; /* Number of encoded samples */ gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ gsize count; /* Codec transfer buffer counter */ @@ -152,8 +149,6 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) } if (self->data) { - if (self->data->cfg.codec == CFG_CODEC_SBC) - sbc_finish(&self->data->sbc); g_free(self->data); self->data = NULL; } @@ -268,25 +263,13 @@ static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, if (cfg->codec != CFG_CODEC_SBC) return -1; - /* FIXME: init using flags? */ - sbc_init(&data->sbc, 0); - data->sbc.rate = cfg->rate; - data->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; - if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) - data->sbc.joint = 1; - data->sbc.allocation = sbc->allocation; - data->sbc.subbands = sbc->subbands; - data->sbc.blocks = sbc->blocks; - data->sbc.bitpool = sbc->bitpool; - data->codesize = data->sbc.subbands * data->sbc.blocks * - data->sbc.channels * 2; data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); GST_DEBUG_OBJECT(self, "Codec parameters: " "\tallocation=%u\n\tsubbands=%u\n " "\tblocks=%u\n\tbitpool=%u\n", - data->sbc.allocation, data->sbc.subbands, - data->sbc.blocks, data->sbc.bitpool); + sbc->allocation, sbc->subbands, + sbc->blocks, sbc->bitpool); return 0; } -- cgit From ea39736b353ba409d03e8f86b7ca6ca1e746fecd Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 29 Oct 2007 15:02:26 +0000 Subject: Add bitpool capability. --- audio/gsta2dpsink.c | 7 +++++-- audio/gstsbcenc.c | 7 ++++++- audio/gstsbcparse.c | 23 ++++++++++++++++++++--- audio/gstsbcutil.c | 9 +++++++++ audio/gstsbcutil.h | 2 ++ 5 files changed, 42 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 0ed39a66..be81efe8 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -112,7 +112,8 @@ static GstStaticPadTemplate a2dp_sink_factory = "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }; " + "allocation = (string) { snr, loudness }," + "bitpool = (int) [ 2, 64 ]; " "audio/mpeg, " "mpegversion = (int) 1, " "layer = (int) [ 1, 3 ], " @@ -335,7 +336,9 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, value = gst_structure_get_value(structure, "blocks"); sbc->blocks = g_value_get_int(value); - sbc->bitpool = 32; + + value = gst_structure_get_value(structure, "bitpool"); + sbc->bitpool = g_value_get_int(value); pkt->length = sizeof(*cfg) + sizeof(*sbc); pkt->type = PKT_TYPE_CFG_REQ; diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 628d15c9..69e67aeb 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -33,6 +33,7 @@ #define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO #define SBC_ENC_DEFAULT_BLOCKS 16 #define SBC_ENC_DEFAULT_SUB_BANDS 8 +#define SBC_ENC_DEFAULT_BITPOOL 53 #define SBC_ENC_DEFAULT_ALLOCATION CFG_ALLOCATION_AUTO GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); @@ -111,7 +112,8 @@ static GstStaticPadTemplate sbc_enc_src_factory = "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }")); + "allocation = (string) { snr, loudness }," + "bitpool = (int) [ 2, 64 ]")); static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) @@ -145,6 +147,8 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) else enc->sbc.allocation = enc->allocation; + enc->sbc.bitpool = SBC_ENC_DEFAULT_BITPOOL; + mode = gst_sbc_get_mode_string(enc->sbc.joint); allocation = gst_sbc_get_allocation_string(enc->sbc.allocation); @@ -155,6 +159,7 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) "subbands", G_TYPE_INT, enc->sbc.subbands, "blocks", G_TYPE_INT, enc->sbc.blocks, "allocation", G_TYPE_STRING, allocation, + "bitpool", G_TYPE_INT, enc->sbc.bitpool, NULL); return src_caps; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index a15a75fb..42ae9550 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -53,16 +53,17 @@ static GstStaticPadTemplate sbc_parse_src_factory = "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }")); + "allocation = (string) { snr, loudness }," + "bitpool = (int) [ 2, 64 ]")); -/* reates a fixed caps from the caps given. */ +/* Creates a fixed caps from the caps given. */ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) { GstCaps *result; GstStructure *structure; const GValue *value; gboolean error = FALSE; - gint temp, rate, channels, blocks, subbands; + gint temp, rate, channels, blocks, subbands, bitpool; const gchar* allocation = NULL; const gchar* mode = NULL; const gchar* error_message = NULL; @@ -133,6 +134,20 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) subbands = temp; } + if (!gst_structure_has_field(structure, "bitpool")) { + error = TRUE; + error_message = "no bitpool"; + goto error; + } else { + value = gst_structure_get_value(structure, "bitpool"); + if (GST_VALUE_HOLDS_INT_RANGE(value)) { + temp = gst_sbc_select_bitpool_from_range(value); + } else { + temp = g_value_get_int(value); + } + bitpool = temp; + } + if (!gst_structure_has_field(structure, "allocation")) { error = TRUE; error_message = "no allocation."; @@ -173,11 +188,13 @@ error: "blocks", G_TYPE_INT, blocks, "subbands", G_TYPE_INT, subbands, "allocation", G_TYPE_STRING, allocation, + "bitpool", G_TYPE_INT, bitpool, NULL); parse->sbc.rate = rate; parse->sbc.channels = channels; parse->sbc.blocks = blocks; parse->sbc.subbands = subbands; + parse->sbc.bitpool = bitpool; parse->sbc.joint = gst_sbc_get_mode_int(mode); parse->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index c795aedc..e55220c3 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -104,6 +104,15 @@ gint gst_sbc_select_subbands_from_range(const GValue *value) return gst_value_get_int_range_max(value); } +/* + * Selects one bitpool option from a range + * TODO - use a better approach to this (it is selecting the maximum value) + */ +gint gst_sbc_select_bitpool_from_range(const GValue *value) +{ + return gst_value_get_int_range_max(value); +} + /* * Selects one allocation mode from the ones on the list * TODO - use a better approach diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 70169a74..98f202f0 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -35,6 +35,8 @@ gint gst_sbc_select_blocks_from_range(const GValue *value); gint gst_sbc_select_subbands_from_list(const GValue *value); gint gst_sbc_select_subbands_from_range(const GValue *value); +gint gst_sbc_select_bitpool_from_range(const GValue *value); + const gchar *gst_sbc_get_allocation_from_list(const GValue *value); gint gst_sbc_get_allocation_mode_int(const gchar *allocation); const gchar *gst_sbc_get_allocation_string(int alloc); -- cgit From 9a5725b6306e1d69a89d988af94bd3efe1ef5de6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 1 Nov 2007 13:56:51 +0000 Subject: Fix buffers timestamps in sbcenc. --- audio/gstsbcenc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 69e67aeb..681a2291 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -217,6 +217,7 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) goto done; memcpy(GST_BUFFER_DATA(output), enc->sbc.data, enc->sbc.len); + GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); if (res != GST_FLOW_OK) -- cgit From 96d6078ada20a76f885ea04893aac5f0ca5fe48d Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 1 Nov 2007 19:45:00 +0000 Subject: Fix sbc negotiation and improves buffer handling by using GstAdapter. --- audio/gsta2dpsink.c | 84 +++++++++++++++++++++++++++++++++++++++++- audio/gsta2dpsink.h | 3 ++ audio/gstsbcenc.c | 104 +++++++++++++++++++++++++++++++++++++++++++++------- audio/gstsbcenc.h | 2 + audio/gstsbcparse.c | 10 +---- audio/gstsbcutil.c | 63 +++++++++++++------------------ audio/gstsbcutil.h | 14 +++++-- 7 files changed, 213 insertions(+), 67 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index be81efe8..e605158f 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -37,6 +37,7 @@ #include "ipc.h" #include "rtp.h" +#include "gstsbcutil.h" #include "gsta2dpsink.h" @@ -143,10 +144,17 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) self->watch_id = 0; } + if (self->stream) { + g_io_channel_flush(self->stream, NULL); + g_io_channel_close(self->stream); + g_io_channel_unref(self->stream); + self->stream = NULL; + } + if (self->server) { g_io_channel_close(self->server); g_io_channel_unref(self->server); - self->stream = NULL; + self->server = NULL; } if (self->data) { @@ -154,6 +162,16 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) self->data = NULL; } + if (self->sbc) { + g_free(self->sbc); + self->sbc = NULL; + } + + if (self->dev_caps) { + gst_caps_unref(self->dev_caps); + self->dev_caps = NULL; + } + return TRUE; } @@ -250,6 +268,25 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) return -EINVAL; } +static void gst_a2dp_sink_check_dev_caps(GstA2dpSink *self) +{ + GstStructure *structure; + GstCaps *dev_caps; + gint channels; + + structure = gst_caps_get_structure(self->dev_caps, 0); + if (!gst_structure_get_int(structure, "channels", &channels)) + channels = 2; /* FIXME how to get channels */ + dev_caps = gst_sbc_caps_from_sbc(&(self->data->cfg), self->sbc, + channels); + + self->new_dev_caps = TRUE; + gst_caps_unref(self->dev_caps); + self->dev_caps = gst_caps_ref(dev_caps); + + +} + static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, struct ipc_codec_sbc *sbc) { @@ -406,6 +443,9 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) } memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); + memcpy(sink->sbc, sbc, sizeof(struct ipc_codec_sbc)); + + gst_a2dp_sink_check_dev_caps(sink); GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" "fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", @@ -414,6 +454,7 @@ static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) sink->data->cfg.rate); if (sink->data->cfg.codec == CFG_CODEC_SBC) { + /* FIXME is this necessary? */ ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); if (ret < 0) return FALSE; @@ -577,8 +618,12 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) self->data = g_new0(struct bluetooth_data, 1); memset(self->data, 0, sizeof(struct bluetooth_data)); + self->sbc = g_new0(struct ipc_codec_sbc, 1); + self->stream = NULL; self->con_state = NOT_CONFIGURED; + self->new_dev_caps = FALSE; + self->dev_caps = NULL; self->waiting_con_conf = FALSE; @@ -718,8 +763,16 @@ static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) GstA2dpSink *self = GST_A2DP_SINK(basesink); GST_A2DP_SINK_MUTEX_LOCK(self); - if (self->con_state == NOT_CONFIGURED) + if (self->con_state == NOT_CONFIGURED) { gst_a2dp_sink_start_dev_conf(self, caps); + + if (self->dev_caps) + gst_caps_unref(self->dev_caps); + self->dev_caps = gst_caps_ref(caps); + + /* we suppose the device will accept this caps */ + self->new_dev_caps = FALSE; + } GST_A2DP_SINK_MUTEX_UNLOCK(self); return TRUE; @@ -735,6 +788,30 @@ static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) return TRUE; } +static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink, + guint64 offset, guint size, GstCaps* caps, + GstBuffer **buf) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + + *buf = gst_buffer_new_and_alloc(size); + if (!(*buf)) { + GST_ERROR_OBJECT(self, "buffer allocation failed"); + return GST_FLOW_ERROR; + } + + if (self->new_dev_caps && self->dev_caps) { + GST_INFO_OBJECT(self, "new caps from device"); + gst_buffer_set_caps(*buf, self->dev_caps); + self->new_dev_caps = FALSE; + } else + gst_buffer_set_caps(*buf, caps); + + GST_BUFFER_OFFSET(*buf) = offset; + + return GST_FLOW_OK; +} + static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); @@ -754,6 +831,8 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); + basesink_class->buffer_alloc = + GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc); g_object_class_install_property(object_class, PROP_DEVICE, g_param_spec_string("device", "Device", @@ -768,6 +847,7 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { self->device = NULL; self->data = NULL; + self->sbc = NULL; self->stream = NULL; self->con_state = NOT_CONFIGURED; diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 76d512b9..ea750406 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -49,9 +49,12 @@ struct _GstA2dpSink { GIOChannel *stream; struct bluetooth_data *data; + struct ipc_codec_sbc *sbc; GIOChannel *server; gint con_state; + GstCaps *dev_caps; + gboolean new_dev_caps; GCond *con_conf_end; gboolean waiting_con_conf; diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 681a2291..0e9daed1 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -115,7 +115,6 @@ static GstStaticPadTemplate sbc_enc_src_factory = "allocation = (string) { snr, loudness }," "bitpool = (int) [ 2, 64 ]")); - static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) { gint rate; @@ -188,24 +187,83 @@ error: return FALSE; } +gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) +{ + GstStructure *structure; + gint rate, channels, subbands, blocks, bitpool; + const gchar* mode; + const gchar* allocation; + + g_assert(gst_caps_is_fixed(caps)); + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int(structure, "subbands", &subbands)) + return FALSE; + if (!gst_structure_get_int(structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + return FALSE; + + if (!(mode = gst_structure_get_string(structure, "mode"))) + return FALSE; + if (!(allocation = gst_structure_get_string(structure, "allocation"))) + return FALSE; + + enc->sbc.rate = rate; + enc->sbc.channels = channels; + enc->blocks = blocks; + enc->sbc.subbands = subbands; + enc->sbc.bitpool = bitpool; + enc->mode = gst_sbc_get_mode_int(mode); + enc->allocation = gst_sbc_get_allocation_mode_int(allocation); + + return TRUE; +} + +static gboolean gst_sbc_enc_change_caps(GstSbcEnc *enc, GstCaps *caps) +{ + GST_INFO_OBJECT(enc, "Changing srcpad caps (renegotiation)"); + + if (!gst_pad_accept_caps(enc->srcpad, caps)) { + GST_WARNING_OBJECT(enc, "Src pad refused caps"); + return FALSE; + } + + if (!gst_sbc_enc_fill_sbc_params(enc, caps)) { + GST_ERROR_OBJECT(enc, "couldn't get sbc parameters from caps"); + return FALSE; + } + + return TRUE; +} + static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) { GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); + GstAdapter *adapter = enc->adapter; GstFlowReturn res = GST_FLOW_OK; - guint size, offset = 0; - guint8 *data; + gint codesize = enc->sbc.subbands * enc->sbc.blocks * enc->sbc.channels * 2; - data = GST_BUFFER_DATA(buffer); - size = GST_BUFFER_SIZE(buffer); + gst_adapter_push(adapter, buffer); - while (offset < size) { + while (gst_adapter_available(adapter) >= codesize && res == GST_FLOW_OK) { GstBuffer *output; GstCaps *caps; + const guint8 *data; int consumed; - consumed = sbc_encode(&enc->sbc, data + offset, size - offset); - if (consumed <= 0) + data = gst_adapter_peek(adapter, codesize); + consumed = sbc_encode(&enc->sbc, (gpointer) data, codesize); + if (consumed <= 0) { + GST_ERROR ("comsumed < 0, codesize: %d", codesize); break; + } + gst_adapter_flush(adapter, consumed); caps = GST_PAD_CAPS(enc->srcpad); @@ -216,21 +274,26 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) if (res != GST_FLOW_OK) goto done; + if (!gst_caps_is_equal(caps, GST_BUFFER_CAPS(output))) + if (!gst_sbc_enc_change_caps(enc, + GST_BUFFER_CAPS(output))) { + res = GST_FLOW_ERROR; + GST_ERROR_OBJECT(enc, "couldn't renegotiate caps"); + goto done; + } + memcpy(GST_BUFFER_DATA(output), enc->sbc.data, enc->sbc.len); GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); - if (res != GST_FLOW_OK) + if (res != GST_FLOW_OK) { + GST_ERROR_OBJECT(enc, "pad pushing failed"); goto done; + } - offset += consumed; } - if (offset < size) - res = GST_FLOW_ERROR; - done: - gst_buffer_unref(buffer); gst_object_unref(enc); return res; @@ -259,6 +322,16 @@ static GstStateChangeReturn sbc_enc_change_state(GstElement *element, return parent_class->change_state(element, transition); } +static void gst_sbc_enc_dispose(GObject *object) +{ + GstSbcEnc *enc = GST_SBC_ENC(object); + + if (enc->adapter != NULL) + g_object_unref (G_OBJECT (enc->adapter)); + + enc->adapter = NULL; +} + static void gst_sbc_enc_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -333,6 +406,7 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property); object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property); + object_class->dispose = GST_DEBUG_FUNCPTR(gst_sbc_enc_dispose); element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); @@ -375,4 +449,6 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) self->blocks = SBC_ENC_DEFAULT_BLOCKS; self->mode = SBC_ENC_DEFAULT_MODE; self->allocation = SBC_ENC_DEFAULT_ALLOCATION; + + self->adapter = gst_adapter_new(); } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index 0a95bcef..c5fc6bcc 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -22,6 +22,7 @@ */ #include +#include #include "sbc.h" #include "ipc.h" @@ -48,6 +49,7 @@ struct _GstSbcEnc { GstPad *sinkpad; GstPad *srcpad; + GstAdapter *adapter; gint mode; gint blocks; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 42ae9550..185cda03 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -78,8 +78,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) value = gst_structure_get_value(structure, "rate"); if (GST_VALUE_HOLDS_LIST(value)) { temp = gst_sbc_select_rate_from_list(value); - } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { - temp = gst_sbc_select_rate_from_range(value); } else { temp = g_value_get_int(value); } @@ -92,9 +90,7 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "channels"); - if (GST_VALUE_HOLDS_LIST(value)) { - temp = gst_sbc_select_channels_from_list(value); - } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + if (GST_VALUE_HOLDS_INT_RANGE(value)) { temp = gst_sbc_select_channels_from_range(value); } else { temp = g_value_get_int(value); @@ -110,8 +106,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) value = gst_structure_get_value(structure, "blocks"); if (GST_VALUE_HOLDS_LIST(value)) { temp = gst_sbc_select_blocks_from_list(value); - } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { - temp = gst_sbc_select_blocks_from_range(value); } else { temp = g_value_get_int(value); } @@ -126,8 +120,6 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) value = gst_structure_get_value(structure, "subbands"); if (GST_VALUE_HOLDS_LIST(value)) { temp = gst_sbc_select_subbands_from_list(value); - } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { - temp = gst_sbc_select_subbands_from_range(value); } else { temp = g_value_get_int(value); } diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index e55220c3..1d7a1227 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -38,25 +38,6 @@ gint gst_sbc_select_rate_from_list(const GValue *value) return g_value_get_int(gst_value_list_get_value(value, size-1)); } -/* - * Selects one rate from a range of possible rates - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint gst_sbc_select_rate_from_range(const GValue *value) -{ - return gst_value_get_int_range_max(value); -} - -/* - * Selects one number of channels from a list of possible numbers - * TODO - use a better approach to this (it is selecting the last element) - */ -gint gst_sbc_select_channels_from_list(const GValue *value) -{ - guint size = gst_value_list_get_size(value); - return g_value_get_int(gst_value_list_get_value(value, size-1)); -} - /* * Selects one number of channels option from a range of possible numbers * TODO - use a better approach to this (it is selecting the maximum value) @@ -76,15 +57,6 @@ gint gst_sbc_select_blocks_from_list(const GValue *value) return g_value_get_int(gst_value_list_get_value(value, size-1)); } -/* - * Selects one blocks option from a range of possible blocks - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint gst_sbc_select_blocks_from_range(const GValue *value) -{ - return gst_value_get_int_range_max(value); -} - /* * Selects one number of subbands from a list * TODO - use a better approach to this (it is selecting the last element) @@ -95,15 +67,6 @@ gint gst_sbc_select_subbands_from_list(const GValue *value) return g_value_get_int(gst_value_list_get_value(value, size-1)); } -/* - * Selects one subbands option from a range - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint gst_sbc_select_subbands_from_range(const GValue *value) -{ - return gst_value_get_int_range_max(value); -} - /* * Selects one bitpool option from a range * TODO - use a better approach to this (it is selecting the maximum value) @@ -187,8 +150,32 @@ const gchar *gst_sbc_get_allocation_string(int alloc) case CFG_ALLOCATION_SNR: return "snr"; case CFG_ALLOCATION_AUTO: - return NULL; /* TODO what should be selected here? */ + return "loudness"; /* TODO what should be selected here? */ default: return NULL; } } + +GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, + struct ipc_codec_sbc *sbc, gint channels) +{ + GstCaps *caps; + const gchar *mode_str; + const gchar *allocation_str; + + mode_str = gst_sbc_get_mode_string(cfg->mode); + allocation_str = gst_sbc_get_allocation_string(sbc->allocation); + + caps = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, cfg->rate, + "channels", G_TYPE_INT, channels, + "mode", G_TYPE_STRING, mode_str, + "subbands", G_TYPE_INT, sbc->subbands, + "blocks", G_TYPE_INT, sbc->blocks, + "allocation", G_TYPE_STRING, allocation_str, + "bitpool", G_TYPE_INT, sbc->bitpool, + NULL); + + return caps; +} + diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 98f202f0..0c91fe82 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -22,18 +22,20 @@ */ #include +#include "sbc.h" + +struct ipc_data_cfg; /* FIXME can't include ipc.h */ +struct ipc_codec_sbc; gint gst_sbc_select_rate_from_list(const GValue *value); -gint gst_sbc_select_rate_from_range(const GValue *value); -gint gst_sbc_select_channels_from_list(const GValue *value); gint gst_sbc_select_channels_from_range(const GValue *value); gint gst_sbc_select_blocks_from_list(const GValue *value); -gint gst_sbc_select_blocks_from_range(const GValue *value); gint gst_sbc_select_subbands_from_list(const GValue *value); -gint gst_sbc_select_subbands_from_range(const GValue *value); + +gint gst_sbc_select_bitpool_from_range(const GValue *value); gint gst_sbc_select_bitpool_from_range(const GValue *value); @@ -44,3 +46,7 @@ const gchar *gst_sbc_get_allocation_string(int alloc); const gchar *gst_sbc_get_mode_from_list(const GValue *value); gint gst_sbc_get_mode_int(const gchar *mode); const gchar *gst_sbc_get_mode_string(int joint); + +GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, struct ipc_codec_sbc *sbc, + gint channels); + -- cgit From 1f1e900f488c2925b8949911b30f363a1fb4dee0 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 5 Nov 2007 17:02:16 +0000 Subject: Fix packet lenght to be the output MTU. --- audio/avdtp.c | 24 +++++++++++++++--------- audio/avdtp.h | 3 ++- audio/unix.c | 8 ++++---- 3 files changed, 21 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 00045820..f68561e9 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -308,7 +308,8 @@ struct stream_callback { struct avdtp_stream { int sock; - uint16_t mtu; + uint16_t imtu; + uint16_t omtu; struct avdtp *session; struct avdtp_local_sep *lsep; uint8_t rseid; @@ -605,7 +606,7 @@ static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, } static void handle_transport_connect(struct avdtp *session, int sock, - uint16_t mtu) + uint16_t imtu, uint16_t omtu) { struct avdtp_stream *stream = session->pending_open; struct avdtp_local_sep *sep = stream->lsep; @@ -629,7 +630,8 @@ static void handle_transport_connect(struct avdtp *session, int sock, } stream->sock = sock; - stream->mtu = mtu; + stream->omtu = omtu; + stream->imtu = imtu; if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, NULL, sep->user_data); @@ -690,7 +692,7 @@ static void avdtp_sep_set_state(struct avdtp *session, } session->streams = g_slist_remove(session->streams, stream); if (session->pending_open == stream) - handle_transport_connect(session, -1, 0); + handle_transport_connect(session, -1, 0, 0); if (session->req && session->req->stream == stream) session->req->stream = NULL; stream_free(stream); @@ -1573,7 +1575,7 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, avrcp_connect(dev); } else if (session->pending_open) - handle_transport_connect(session, sk, l2o.imtu); + handle_transport_connect(session, sk, l2o.imtu, l2o.omtu); else { err = -EIO; goto failed; @@ -2274,7 +2276,8 @@ gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, } gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, - uint16_t *mtu, GSList **caps) + uint16_t *imtu, uint16_t *omtu, + GSList **caps) { if (stream->sock < 0) return FALSE; @@ -2282,8 +2285,11 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, if (sock) *sock = stream->sock; - if (mtu) - *mtu = stream->mtu; + if (omtu) + *omtu = stream->omtu; + + if (imtu) + *imtu = stream->imtu; if (caps) *caps = stream->caps; @@ -2797,7 +2803,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) session = avdtp_get_internal(&src, &dst); if (session->pending_open && session->pending_open->open_acp) { - handle_transport_connect(session, cli_sk, l2o.imtu); + handle_transport_connect(session, cli_sk, l2o.imtu, l2o.omtu); return TRUE; } diff --git a/audio/avdtp.h b/audio/avdtp.h index 99b95477..88684f28 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -216,7 +216,8 @@ gboolean avdtp_stream_remove_cb(struct avdtp *session, unsigned int id); gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, - uint16_t *mtu, GSList **caps); + uint16_t *imtu, uint16_t *omtu, + GSList **caps); gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, struct avdtp_service_capability *cap); diff --git a/audio/unix.c b/audio/unix.c index eb849ddc..71eb570a 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -305,7 +305,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct ipc_codec_sbc *sbc = (void *) cfg->data; struct a2dp_data *a2dp = &client->d.a2dp; int fd; - uint16_t mtu; + uint16_t imtu, omtu; GSList *caps; client->req_id = 0; @@ -321,13 +321,11 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, a2dp->sep = sep; a2dp->stream = stream; - if (!avdtp_stream_get_transport(stream, &fd, &mtu, &caps)) { + if (!avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, &caps)) { error("Unable to get stream transport"); goto failed; } - cfg->pkt_len = mtu; - for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { cap = caps->data; if (cap->category == AVDTP_MEDIA_CODEC) { @@ -342,6 +340,8 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, goto failed; } + /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ + cfg->pkt_len = omtu; cfg->fd_opt = CFG_FD_OPT_WRITE; sbc_cap = (void *) codec_cap; -- cgit From dff420207b69768a305ed014f7c7648d027d6b38 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 9 Nov 2007 08:45:57 +0000 Subject: A2DP wakeup refactoring & cleanup (patch from Kai Vehmanen ) --- audio/pcm_bluetooth.c | 166 +++++++++++++++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 64 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6f9bbe0b..b3c05cf8 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -91,6 +92,7 @@ struct bluetooth_data { pthread_t hw_thread; /* Makes virtual hw pointer move */ int pipefd[2]; /* Inter thread communication */ int stopped; + sig_atomic_t reset; /* Request XRUN handling */ }; static int bluetooth_start(snd_pcm_ioplug_t *io) @@ -116,6 +118,9 @@ static void *playback_hw_thread(void *param) struct pollfd fds[2]; int poll_timeout; + data->server.events = POLLIN; + /* note: only errors for data->stream.events */ + fds[0] = data->server; fds[1] = data->stream; @@ -136,6 +141,13 @@ static void *playback_hw_thread(void *param) if (data->stopped) goto iter_sleep; + if (data->reset) { + DBG("Handle XRUN in hw-thread."); + data->reset = 0; + gettimeofday(&start, 0); + prev_periods = 0; + } + gettimeofday(&cur, 0); timersub(&cur, &start, &delta); @@ -145,14 +157,17 @@ static void *playback_hw_thread(void *param) if (periods > prev_periods) { char c = 'w'; + int frags = periods - prev_periods, n; - data->hw_ptr += (periods - prev_periods) * - data->io.period_size; + data->hw_ptr += frags * data->io.period_size; data->hw_ptr %= data->io.buffer_size; - /* Notify user that hardware pointer has moved */ - if (write(data->pipefd[1], &c, 1) < 0) - pthread_testcancel(); + for (n = 0; n < frags; n++) { + /* Notify user that hardware pointer + * has moved * */ + if (write(data->pipefd[1], &c, 1) < 0) + pthread_testcancel(); + } /* Reset point of reference to avoid too big values * that wont fit an unsigned int */ @@ -167,6 +182,7 @@ static void *playback_hw_thread(void *param) iter_sleep: /* sleep up to one period interval */ ret = poll(fds, 2, poll_timeout); + if (ret < 0) { SNDERR("poll error: %s (%d)", strerror(errno), errno); if (errno != EINTR) @@ -329,6 +345,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", io->period_size, io->buffer_size); + data->reset = 0; + if (io->stream == SND_PCM_STREAM_PLAYBACK) /* If not null for playback, xmms doesn't display time * correctly */ @@ -416,7 +434,7 @@ static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) + unsigned short *revents) { assert(pfds && nfds == 1 && revents); @@ -446,7 +464,7 @@ static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io, static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) + unsigned short *revents) { static char buf[1]; int ret; @@ -469,7 +487,8 @@ static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -526,7 +545,8 @@ done: static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; struct ipc_data_cfg cfg = data->cfg; @@ -584,7 +604,8 @@ done: static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { snd_pcm_uframes_t ret = 0; return ret; @@ -609,9 +630,11 @@ static int avdtp_write(struct bluetooth_data *data) header->timestamp = htonl(a2dp->nsamples); header->ssrc = htonl(1); - ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); - if (ret == -1) + ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); + if (ret < 0) { + DBG("send returned %d errno %s.", ret, strerror(errno)); ret = -errno; + } /* Reset buffer of data to send */ a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); @@ -629,18 +652,20 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, struct bluetooth_data *data = io->private_data; struct bluetooth_a2dp *a2dp = &data->a2dp; snd_pcm_sframes_t ret = 0; - snd_pcm_uframes_t frames_to_read; + snd_pcm_uframes_t frames_to_read, frames_left = size; int frame_size, encoded; uint8_t *buff; DBG("areas->step=%u areas->first=%u offset=%lu size=%lu", areas->step, areas->first, offset, size); - DBG("hw_ptr=%lu appl_ptr=%lu", io->hw_ptr, io->appl_ptr); + DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr, + io->appl_ptr - io->hw_ptr); if (io->hw_ptr > io->appl_ptr) { ret = bluetooth_playback_stop(io); if (ret == 0) ret = -EPIPE; + data->reset = 1; goto done; } @@ -651,66 +676,76 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, snd_pcm_sw_params_malloc(&swparams); if (!snd_pcm_sw_params_current(io->pcm, swparams) && - !snd_pcm_sw_params_get_start_threshold(swparams, &threshold)) { + !snd_pcm_sw_params_get_start_threshold(swparams, + &threshold)) { if (io->appl_ptr >= threshold) { ret = snd_pcm_start(io->pcm); if (ret != 0) goto done; } } + snd_pcm_sw_params_free(swparams); } - frame_size = areas->step / 8; - if ((data->count + size * frame_size) <= a2dp->codesize) - frames_to_read = size; - else - frames_to_read = (a2dp->codesize - data->count) / frame_size; + while (frames_left > 0) { + frame_size = areas->step / 8; - DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); - DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); + if ((data->count + frames_left * frame_size) <= a2dp->codesize) + frames_to_read = frames_left; + else + frames_to_read = (a2dp->codesize - data->count) / frame_size; - /* FIXME: If state is not streaming then return */ + DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); + DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); - /* Ready for more data */ - buff = (uint8_t *) areas->addr + - (areas->first + areas->step * offset) / 8; - memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); + /* FIXME: If state is not streaming then return */ - /* Remember we have some frames in the pipe now */ - data->count += frames_to_read * frame_size; - if (data->count != a2dp->codesize) { - ret = frames_to_read; - goto done; - } + /* Ready for more data */ + buff = (uint8_t *) areas->addr + + (areas->first + areas->step * (offset + ret)) / 8; + memcpy(data->buffer + data->count, buff, + frame_size * frames_to_read); - /* Enough data to encode (sbc wants 1k blocks) */ - encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize); - if (encoded <= 0) { - DBG("Encoding error %d", encoded); - goto done; - } + /* Remember we have some frames in the pipe now */ + data->count += frames_to_read * frame_size; + if (data->count != a2dp->codesize) { + ret = frames_to_read; + goto done; + } - data->count -= encoded; + /* Enough data to encode (sbc wants 1k blocks) */ + encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize); + if (encoded <= 0) { + DBG("Encoding error %d", encoded); + goto done; + } - DBG("encoded=%d a2dp.sbc.len=%d", encoded, a2dp->sbc.len); + data->count -= encoded; - if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { - ret = avdtp_write(data); - if (ret < 0) { - if (-ret == EPIPE) - ret = -EIO; - goto done; + DBG("encoded=%d a2dp.sbc.len=%d count=%d", encoded, + a2dp->sbc.len, a2dp->count); + + /* Send previously encoded buffer */ + if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { + avdtp_write(data); + DBG("sending packet %d, count %d, pkt_len %u", c, + old_count, data->cfg.pkt_len); } - } - memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); - a2dp->count += a2dp->sbc.len; - a2dp->frame_count++; - a2dp->samples += encoded / frame_size; - a2dp->nsamples += encoded / frame_size; + memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); + a2dp->count += a2dp->sbc.len; + a2dp->frame_count++; + a2dp->samples += encoded / frame_size; + a2dp->nsamples += encoded / frame_size; + + ret += frames_to_read; + frames_left -= frames_to_read; + } - ret = frames_to_read; + /* note: some ALSA apps will get confused otherwise */ + if (ret > size) + ret = size; done: DBG("returning %ld", ret); @@ -847,7 +882,6 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; struct ipc_data_cfg cfg = data->cfg; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, @@ -859,6 +893,13 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; + unsigned int period_list[] = { + 512, /* 3/6ms (mono/stereo 16bit at 44.1kHz) */ + 1024, /* 6/12ms */ + 2048, /* 12/23ms */ + 4096, /* 23/46ms */ + 8192, /* 46/93ms */ + }; int err, channels; /* access type */ @@ -886,18 +927,15 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; - /* supported block sizes: - * - lower limit is A2DP codec size - * - total buffer size is the upper limit (with two periods) */ - err = snd_pcm_ioplug_set_param_minmax(io, - SND_PCM_IOPLUG_HW_PERIOD_BYTES, - a2dp->codesize, - a2dp->codesize); + /* supported block sizes: */ + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + ARRAY_NELEMS(period_list), period_list); if (err < 0) return err; + /* period count fixed to 3 as we don't support prefilling */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 50); + 3, 3); if (err < 0) return err; @@ -1184,7 +1222,7 @@ done: DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len, - data->cfg.sample_size, data->cfg.rate); + data->cfg.sample_size, data->cfg.rate); if (data->cfg.codec == CFG_CODEC_SBC) { ret = bluetooth_a2dp_init(data, sbc); -- cgit From 65086658fce4caf34e85e35a7edf8c312908986d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 9 Nov 2007 12:27:25 +0000 Subject: Use just one possible value in the period size list since multiple options seem to break vls (looks like it's caused by a bug in the ALSA snd_pcm_hw_params_set_period_size_near function) --- audio/pcm_bluetooth.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b3c05cf8..28f5de43 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -894,11 +894,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) SND_PCM_FORMAT_S16_LE }; unsigned int period_list[] = { - 512, /* 3/6ms (mono/stereo 16bit at 44.1kHz) */ - 1024, /* 6/12ms */ - 2048, /* 12/23ms */ - 4096, /* 23/46ms */ - 8192, /* 46/93ms */ + 4096, /* 23/46ms (stereo/mono 16bit at 44.1kHz) */ }; int err, channels; -- cgit From 344e38a9616a11072a123dacb70daf6ab69d5270 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 9 Nov 2007 13:09:09 +0000 Subject: Use clock_gettime instead of gettimeofday --- audio/pcm_bluetooth.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 28f5de43..89ce8965 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,13 @@ #define SCO_RXBUFS 0x04 #endif +#ifndef TIMESPEC_TO_TIMEVAL +# define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ +} +#endif + struct bluetooth_a2dp { sbc_t sbc; /* Codec data */ int codesize; /* SBC codesize */ @@ -115,6 +123,7 @@ static void *playback_hw_thread(void *param) unsigned int prev_periods; double period_time; struct timeval start; + struct timespec start_monotonic; struct pollfd fds[2]; int poll_timeout; @@ -131,11 +140,13 @@ static void *playback_hw_thread(void *param) else poll_timeout = MIN_PERIOD_TIME; - gettimeofday(&start, 0); + clock_gettime(CLOCK_MONOTONIC, &start_monotonic); + TIMESPEC_TO_TIMEVAL(&start, &start_monotonic); while (1) { unsigned int dtime, periods; struct timeval cur, delta; + struct timespec cur_monotonic; int ret; if (data->stopped) @@ -144,11 +155,13 @@ static void *playback_hw_thread(void *param) if (data->reset) { DBG("Handle XRUN in hw-thread."); data->reset = 0; - gettimeofday(&start, 0); + clock_gettime(CLOCK_MONOTONIC, &start_monotonic); + TIMESPEC_TO_TIMEVAL(&start, &start_monotonic); prev_periods = 0; } - gettimeofday(&cur, 0); + clock_gettime(CLOCK_MONOTONIC, &cur_monotonic); + TIMESPEC_TO_TIMEVAL(&cur, &cur_monotonic); timersub(&cur, &start, &delta); -- cgit From 397d6c2b3bc7661f978c1777442d33fd86ada21e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 12 Nov 2007 18:15:59 +0000 Subject: Make sbc codec to write directly in application buffers and so avoiding memcpys. --- audio/gstsbcdec.c | 17 ++++++++++------- audio/gstsbcenc.c | 30 +++++++++++++++++------------- audio/pcm_bluetooth.c | 24 ++++++++++++------------ 3 files changed, 39 insertions(+), 32 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 98504d4d..a60c3e69 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -58,10 +58,11 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) { GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad)); GstFlowReturn res = GST_FLOW_OK; - guint size, offset = 0; + guint size, codesize, offset = 0; guint8 *data; GstClockTime timestamp; + codesize = sbc_get_codesize(&dec->sbc); timestamp = GST_BUFFER_TIMESTAMP(buffer); if (dec->buffer) { @@ -82,10 +83,6 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) GstCaps *caps, *temp; int consumed; - consumed = sbc_decode(&dec->sbc, data + offset, size - offset); - if (consumed <= 0) - break; - caps = gst_caps_new_simple("audio/x-raw-int", "rate", G_TYPE_INT, dec->sbc.rate, "channels", G_TYPE_INT, dec->sbc.channels, @@ -100,14 +97,20 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, GST_BUFFER_OFFSET_NONE, - dec->sbc.len, temp, &output); + codesize, temp, &output); gst_caps_unref(temp); if (res != GST_FLOW_OK) goto done; - memcpy(GST_BUFFER_DATA(output), dec->sbc.data, dec->sbc.len); + consumed = sbc_decode(&dec->sbc, data + offset, size - offset, + GST_BUFFER_DATA(output), codesize, + NULL); + if (consumed <= 0) + break; + + GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(dec->srcpad, output); if (res != GST_FLOW_OK) diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 0e9daed1..e1c480a0 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -134,10 +134,11 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) enc->sbc.rate = rate; enc->sbc.channels = channels; - if (enc->mode == 0) - enc->sbc.joint = CFG_MODE_JOINT_STEREO; - else - enc->sbc.joint = enc->mode; + if (enc->mode == CFG_MODE_AUTO) + enc->mode = CFG_MODE_JOINT_STEREO; + + if (enc->mode == CFG_MODE_MONO || enc->mode == CFG_MODE_JOINT_STEREO) + enc->sbc.joint = 1; enc->sbc.blocks = enc->blocks; enc->sbc.subbands = enc->subbands; @@ -247,8 +248,10 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); GstAdapter *adapter = enc->adapter; GstFlowReturn res = GST_FLOW_OK; - gint codesize = enc->sbc.subbands * enc->sbc.blocks * enc->sbc.channels * 2; + gint codesize, frame_len; + codesize = sbc_get_codesize(&enc->sbc); + frame_len = sbc_get_frame_length(&enc->sbc); gst_adapter_push(adapter, buffer); while (gst_adapter_available(adapter) >= codesize && res == GST_FLOW_OK) { @@ -257,20 +260,22 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) const guint8 *data; int consumed; + caps = GST_PAD_CAPS(enc->srcpad); + + res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, + GST_BUFFER_OFFSET_NONE, + frame_len, caps, &output); + data = gst_adapter_peek(adapter, codesize); - consumed = sbc_encode(&enc->sbc, (gpointer) data, codesize); + consumed = sbc_encode(&enc->sbc, (gpointer) data, codesize, + GST_BUFFER_DATA(output), frame_len, + NULL); if (consumed <= 0) { GST_ERROR ("comsumed < 0, codesize: %d", codesize); break; } gst_adapter_flush(adapter, consumed); - caps = GST_PAD_CAPS(enc->srcpad); - - res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, - GST_BUFFER_OFFSET_NONE, - enc->sbc.len, caps, &output); - if (res != GST_FLOW_OK) goto done; @@ -282,7 +287,6 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) goto done; } - memcpy(GST_BUFFER_DATA(output), enc->sbc.data, enc->sbc.len); GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 89ce8965..04eabf24 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -666,7 +666,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, struct bluetooth_a2dp *a2dp = &data->a2dp; snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read, frames_left = size; - int frame_size, encoded; + int frame_size, encoded, written; uint8_t *buff; DBG("areas->step=%u areas->first=%u offset=%lu size=%lu", @@ -728,30 +728,30 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, } /* Enough data to encode (sbc wants 1k blocks) */ - encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize); + encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize, + a2dp->buffer, sizeof(a2dp->buffer), + &written); if (encoded <= 0) { DBG("Encoding error %d", encoded); goto done; } data->count -= encoded; + a2dp->count += written; + a2dp->frame_count++; + a2dp->samples += encoded / frame_size; + a2dp->nsamples += encoded / frame_size; - DBG("encoded=%d a2dp.sbc.len=%d count=%d", encoded, - a2dp->sbc.len, a2dp->count); + DBG("encoded=%d written=%d count=%d", encoded, + written, a2dp->count); - /* Send previously encoded buffer */ - if (a2dp->count + a2dp->sbc.len >= data->cfg.pkt_len) { + /* No space left for another frame then send */ + if (a2dp->count + written >= data->cfg.pkt_len) { avdtp_write(data); DBG("sending packet %d, count %d, pkt_len %u", c, old_count, data->cfg.pkt_len); } - memcpy(a2dp->buffer + a2dp->count, a2dp->sbc.data, a2dp->sbc.len); - a2dp->count += a2dp->sbc.len; - a2dp->frame_count++; - a2dp->samples += encoded / frame_size; - a2dp->nsamples += encoded / frame_size; - ret += frames_to_read; frames_left -= frames_to_read; } -- cgit From b1c42bfbbcabbb056820490500e78371ceda0818 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 12 Nov 2007 22:21:45 +0000 Subject: Fix bug in sbcenc when changing encoder parameters. --- audio/gstsbcenc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index e1c480a0..1749460d 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -215,6 +215,8 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) if (!(allocation = gst_structure_get_string(structure, "allocation"))) return FALSE; + sbc_finish(&enc->sbc); + sbc_init(&enc->sbc, 0); enc->sbc.rate = rate; enc->sbc.channels = channels; enc->blocks = blocks; -- cgit From 1d85e2973463f7b194a4c31bd9c5954822cf3022 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 13 Nov 2007 19:12:44 +0000 Subject: Fix double-check for HSP instead of checking HFP --- audio/manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 6c23923f..b0cc3340 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -638,7 +638,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) created = TRUE; } - if (!strcmp(uuid, HSP_AG_UUID) || !strcmp(uuid, HSP_AG_UUID)) { + if (!strcmp(uuid, HSP_AG_UUID) || !strcmp(uuid, HFP_AG_UUID)) { if (device->headset) return device; -- cgit From 6d94a5b2e34c55611f2d6ea70c241971e65afc8c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 13 Nov 2007 20:04:12 +0000 Subject: Add sbc_reinit. --- audio/gstsbcenc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 1749460d..54bb7f8a 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -215,8 +215,7 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) if (!(allocation = gst_structure_get_string(structure, "allocation"))) return FALSE; - sbc_finish(&enc->sbc); - sbc_init(&enc->sbc, 0); + sbc_reinit(&enc->sbc, 0); enc->sbc.rate = rate; enc->sbc.channels = channels; enc->blocks = blocks; -- cgit From fad46eca75efef391fbaf18117ef15ee8000dbea Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 20 Nov 2007 17:36:03 +0000 Subject: Fix bug in alsa plugin. --- audio/pcm_bluetooth.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 04eabf24..f660123f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -729,7 +729,8 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, /* Enough data to encode (sbc wants 1k blocks) */ encoded = sbc_encode(&(a2dp->sbc), data->buffer, a2dp->codesize, - a2dp->buffer, sizeof(a2dp->buffer), + a2dp->buffer + a2dp->count, + sizeof(a2dp->buffer) - a2dp->count, &written); if (encoded <= 0) { DBG("Encoding error %d", encoded); @@ -937,8 +938,11 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported block sizes: */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - ARRAY_NELEMS(period_list), period_list); +// err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, +// ARRAY_NELEMS(period_list), period_list); + /* supported block size */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + 512, 512); if (err < 0) return err; -- cgit From 47e2c26cc95d761099c367593bbbd4bc581bf0ac Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 20 Nov 2007 18:02:48 +0000 Subject: Revert commented lines of previous commit. --- audio/pcm_bluetooth.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index f660123f..efe1d2aa 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -938,11 +938,8 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported block sizes: */ -// err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, -// ARRAY_NELEMS(period_list), period_list); - /* supported block size */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - 512, 512); + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + ARRAY_NELEMS(period_list), period_list); if (err < 0) return err; -- cgit From d4e24bf6a3d8af6479abce92fbbf1869a59669aa Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 21 Nov 2007 20:24:11 +0000 Subject: Integrate new ipc API implementation. --- audio/Makefile.am | 14 +- audio/ctl_bluetooth.c | 134 ++++----- audio/device.c | 2 +- audio/gsta2dpsink.c | 746 +++++++++++++++++++++++++++-------------------- audio/gsta2dpsink.h | 8 +- audio/gstsbcenc.c | 276 +++++++++++++----- audio/gstsbcenc.h | 4 +- audio/gstsbcparse.c | 49 ++-- audio/gstsbcutil.c | 174 +++++++++-- audio/gstsbcutil.h | 9 +- audio/ipc.c | 119 ++++++++ audio/ipc.h | 367 ++++++++++++++++------- audio/manager.c | 4 +- audio/pcm_bluetooth.c | 786 +++++++++++++++++++++++++++----------------------- audio/unix.c | 433 ++++++++++----------------- 15 files changed, 1843 insertions(+), 1282 deletions(-) create mode 100644 audio/ipc.c (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 48c2a80f..b04fc302 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -3,7 +3,7 @@ if AUDIOSERVICE if CONFIGFILES confdir = $(sysconfdir)/bluetooth -conf_DATA = audio.service +conf_DATA = audio.service audio.conf endif servicedir = $(libdir)/bluetooth @@ -11,7 +11,7 @@ servicedir = $(libdir)/bluetooth service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ - manager.h manager.c headset.h headset.c ipc.h unix.h unix.c \ + manager.h manager.c headset.h headset.c ipc.h ipc.c unix.h unix.c \ error.h error.c device.h device.c gateway.h gateway.c \ sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h @@ -23,12 +23,12 @@ alsadir = $(libdir)/alsa-lib alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_bluetooth.la -libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c ipc.h rtp.h +libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ -libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c ipc.h +libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_ctl_.* libasound_module_ctl_bluetooth_la_LIBADD = @ALSA_LIBS@ libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @@ -39,12 +39,13 @@ gstreamerdir = $(libdir)/gstreamer-0.10 gstreamer_LTLIBRARIES = libgstbluetooth.la -libgstbluetooth_la_SOURCES = gstbluetooth.c ipc.h rtp.h \ +libgstbluetooth_la_SOURCES = gstbluetooth.c \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ gsta2dpsink.h gsta2dpsink.c \ - gstsbcutil.h gstsbcutil.c + gstsbcutil.h gstsbcutil.c \ + rtp.h ipc.h ipc.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ @@ -58,3 +59,4 @@ INCLUDES = -I$(top_srcdir)/common EXTRA_DIST = audio.service audio.conf audio-api.txt test-audio asound.conf MAINTAINERCLEANFILES = Makefile.in + diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 9e1c320c..5c198b1a 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -65,7 +65,7 @@ static void bluetooth_exit(struct bluetooth_data *data) return; if (data->sock >= 0) - close(data->sock); + bt_audio_service_close(data->sock); free(data); } @@ -141,32 +141,49 @@ static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, } static int bluetooth_send_ctl(struct bluetooth_data *data, - struct ipc_packet *pkt, int len) + uint8_t mode, uint8_t key, struct bt_control_rsp *ctl_rsp) { int ret; + struct bt_control_req *ctl_req = (void *) ctl_rsp; + const char *type; - ret = send(data->sock, pkt, len, MSG_NOSIGNAL); + memset(ctl_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + ctl_req->h.msg_type = BT_CONTROL_REQ; + ctl_req->mode = mode; + ctl_req->key = key; + + ret = send(data->sock, ctl_req, BT_AUDIO_IPC_PACKET_SIZE, MSG_NOSIGNAL); if (ret <= 0) { SYSERR("Unable to request new volume value to server"); return -errno; } - ret = recv(data->sock, pkt, len, 0); + ret = recv(data->sock, ctl_rsp, BT_AUDIO_IPC_PACKET_SIZE, 0); if (ret <= 0) { - SYSERR("Unable to receive new volume value from server"); + SNDERR("Unable to receive new volume value from server"); return -errno; } - if(pkt->type != PKT_TYPE_CTL_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); + type = bt_audio_strmsg(ctl_rsp->h.msg_type); + if (!type) { + SNDERR("Bogus message type %d " + "received from audio service", + ctl_rsp->h.msg_type); return -EINVAL; } - if(pkt->length != sizeof(struct ipc_data_ctl)) { - SNDERR("Unexpected packet length %d received", pkt->length); + if (ctl_rsp->h.msg_type != BT_CONTROL_RSP) { + SNDERR("Unexpected message %s received", type); return -EINVAL; } + if (ctl_rsp->posix_errno != 0) { + SNDERR("BT_CONTROL failed : %s (%d)", + strerror(ctl_rsp->posix_errno), + ctl_rsp->posix_errno); + return -ctl_rsp->posix_errno; + } + return 0; } @@ -174,28 +191,21 @@ static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; - int len, ret; + int ret; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_rsp *rsp = (void *) buf; DBG("ext %p key %ld", ext, key); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); + memset(buf, 0, sizeof(buf)); *value = 0; - pkt->type = PKT_TYPE_CTL_REQ; - pkt->length = sizeof(struct ipc_data_ctl); - ctl = (struct ipc_data_ctl *) pkt->data; - ctl->mode = key; - - if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + ret = bluetooth_send_ctl(data, key, 0, rsp); + if (ret < 0) goto done; - *value = ctl->key; + *value = rsp->key; done: - free(pkt); return ret; } @@ -203,38 +213,31 @@ static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_rsp *rsp = (void *) buf; long current; - int len, ret; + int ret, keyvalue; DBG("ext %p key %ld", ext, key); - if ((ret = bluetooth_read_integer(ext, key, ¤t)) < 0) + ret = bluetooth_read_integer(ext, key, ¤t); + if (ret < 0) return ret; if (*value == current) return 0; - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); - - pkt->length = sizeof(struct ipc_data_ctl); - ctl = (struct ipc_data_ctl *) pkt->data; - ctl->mode = key; - while (*value != current) { - pkt->type = PKT_TYPE_CTL_REQ; - ctl->key = (*value > current) ? CTL_KEY_VOL_UP : CTL_KEY_VOL_DOWN; + keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP : + BT_CONTROL_KEY_VOL_DOWN; - if ((ret = bluetooth_send_ctl(data, pkt, len)) < 0) + ret = bluetooth_send_ctl(data, key, keyvalue, rsp); + if (ret < 0) break; - current = ctl->key; + current = keyvalue; } - free(pkt); return ret; } @@ -242,33 +245,31 @@ static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { struct bluetooth_data *data = ext->private_data; - struct ipc_packet *pkt; - struct ipc_data_ctl *ctl; - int len, ret; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_control_ind *ind = (void *) buf; + int ret; + const char *type; DBG("ext %p id %p", ext, id); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_ctl); - pkt = malloc(len); - memset(pkt, 0, len); - - ret = recv(data->sock, pkt, len, MSG_DONTWAIT); - if (ret <= 0) - return -errno; + memset(buf, 0, sizeof(buf)); - if(pkt->type != PKT_TYPE_CTL_NTFY) { - SNDERR("Unexpected packet type %d received!", pkt->type); + ret = recv(data->sock, ind, BT_AUDIO_IPC_PACKET_SIZE, MSG_DONTWAIT); + type = bt_audio_strmsg(ind->h.msg_type); + if (!type) { + SNDERR("Bogus message type %d " + "received from audio service", + ind->h.msg_type); return -EAGAIN; } - if(pkt->length != sizeof(struct ipc_data_ctl)) { - SNDERR("Unexpected packet length %d received", pkt->length); + if (ind->h.msg_type != BT_CONTROL_IND) { + SNDERR("Unexpected message %s received", type); return -EAGAIN; } - ctl = (struct ipc_data_ctl *) pkt->data; snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, ctl->mode == BLUETOOTH_PLAYBACK ? + snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ? vol_devices[BLUETOOTH_PLAYBACK] : vol_devices[BLUETOOTH_CAPTURE]); *event_mask = SND_CTL_EVENT_MASK_VALUE; @@ -290,10 +291,7 @@ static snd_ctl_ext_callback_t bluetooth_callback = { static int bluetooth_init(struct bluetooth_data *data) { - int sk, err, id; - struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME - }; + int sk; if (!data) return -EINVAL; @@ -302,21 +300,9 @@ static int bluetooth_init(struct bluetooth_data *data) data->sock = -1; - id = abs(getpid() * rand()); - - if ((sk = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { - err = -errno; - SNDERR("Can't open socket"); + sk = bt_audio_service_open(); + if (sk < 0) return -errno; - } - - DBG("Connecting to address: %s", addr.sun_path + 1); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - SNDERR("Can't connect socket"); - close(sk); - return err; - } data->sock = sk; diff --git a/audio/device.c b/audio/device.c index c86f711d..a5b9b0a9 100644 --- a/audio/device.c +++ b/audio/device.c @@ -450,7 +450,6 @@ static headset_state_t ipc_to_hs_state(uint8_t ipc_state) return HEADSET_STATE_DISCONNECTED; } } -#endif static uint8_t avdtp_to_ipc_state(avdtp_state_t state) { @@ -504,6 +503,7 @@ uint8_t device_get_state(struct device *dev) return STATE_DISCONNECTED; } +#endif gboolean device_is_connected(struct device *dev, const char *interface) { diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index e605158f..535f6c5e 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -41,14 +41,6 @@ #include "gsta2dpsink.h" -enum { - NOT_CONFIGURED, - CONFIGURING_INIT, - CONFIGURING_SENT_CONF, - CONFIGURING_RCVD_DEV_CONF, - CONFIGURED -}; - GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug @@ -62,24 +54,10 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); g_mutex_unlock (s->sink_lock); \ } G_STMT_END -#define GST_A2DP_SINK_WAIT_CON_END(s) G_STMT_START { \ - s->waiting_con_conf = TRUE; \ - g_cond_wait (s->con_conf_end, s->sink_lock); \ - s->waiting_con_conf = FALSE; \ - } G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_FAIL(s) G_STMT_START { \ - s->con_state = NOT_CONFIGURED; \ - g_cond_signal (s->con_conf_end); \ - } G_STMT_END - -#define GST_A2DP_SINK_CONFIGURATION_SUCCESS(s) G_STMT_START { \ - s->con_state = CONFIGURED; \ - g_cond_signal (s->con_conf_end); \ - } G_STMT_END struct bluetooth_data { - struct ipc_data_cfg cfg; /* Bluetooth device config */ + struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */ + gint link_mtu; int samples; /* Number of encoded samples */ gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ gsize count; /* Codec transfer buffer counter */ @@ -121,6 +99,13 @@ static GstStaticPadTemplate a2dp_sink_factory = "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ]")); +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, + const bt_audio_msg_header_t *msg); +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type); + + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -137,8 +122,6 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) GST_INFO_OBJECT(self, "stop"); - self->con_state = NOT_CONFIGURED; - if (self->watch_id != 0) { g_source_remove(self->watch_id); self->watch_id = 0; @@ -152,7 +135,7 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) } if (self->server) { - g_io_channel_close(self->server); + bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); g_io_channel_unref(self->server); self->server = NULL; } @@ -162,9 +145,9 @@ static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) self->data = NULL; } - if (self->sbc) { - g_free(self->sbc); - self->sbc = NULL; + if (self->stream_caps) { + gst_caps_unref(self->stream_caps); + self->stream_caps = NULL; } if (self->dev_caps) { @@ -185,12 +168,6 @@ static void gst_a2dp_sink_finalize(GObject *object) if (self->device) g_free(self->device); - /* unlock any thread waiting for this signal */ - GST_A2DP_SINK_MUTEX_LOCK(self); - GST_A2DP_SINK_CONFIGURATION_FAIL(self); - GST_A2DP_SINK_MUTEX_UNLOCK(self); - - g_cond_free(self->con_conf_end); g_mutex_free(self->sink_lock); G_OBJECT_CLASS(parent_class)->finalize(object); @@ -232,19 +209,10 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) { - char cmsg_b[CMSG_SPACE(sizeof(int))], m; - int err, ret, stream_fd; - struct iovec iov = { &m, sizeof(m) }; - struct msghdr msgh; - struct cmsghdr *cmsg; - - memset(&msgh, 0, sizeof(msgh)); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_control = &cmsg_b; - msgh.msg_controllen = CMSG_LEN(sizeof(int)); - - ret = recvmsg(g_io_channel_unix_get_fd(sink->server), &msgh, 0); + int err, ret; + + ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server)); + if (ret < 0) { err = errno; GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", @@ -252,76 +220,20 @@ static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) return -err; } - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) { - stream_fd = (*(int *) CMSG_DATA(cmsg)); - sink->stream = g_io_channel_unix_new(stream_fd); - - GST_DEBUG_OBJECT(sink, "stream_fd=%d", stream_fd); - return 0; - } - } - - return -EINVAL; -} - -static void gst_a2dp_sink_check_dev_caps(GstA2dpSink *self) -{ - GstStructure *structure; - GstCaps *dev_caps; - gint channels; - - structure = gst_caps_get_structure(self->dev_caps, 0); - if (!gst_structure_get_int(structure, "channels", &channels)) - channels = 2; /* FIXME how to get channels */ - dev_caps = gst_sbc_caps_from_sbc(&(self->data->cfg), self->sbc, - channels); - - self->new_dev_caps = TRUE; - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_ref(dev_caps); - - -} - -static int gst_a2dp_sink_bluetooth_a2dp_init(GstA2dpSink *self, - struct ipc_codec_sbc *sbc) -{ - struct bluetooth_data *data = self->data; - struct ipc_data_cfg *cfg = &data->cfg; - - if (cfg == NULL) { - GST_ERROR_OBJECT(self, "Error getting codec parameters"); - return -1; - } - - if (cfg->codec != CFG_CODEC_SBC) - return -1; - - data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - - GST_DEBUG_OBJECT(self, "Codec parameters: " - "\tallocation=%u\n\tsubbands=%u\n " - "\tblocks=%u\n\tbitpool=%u\n", - sbc->allocation, sbc->subbands, - sbc->blocks, sbc->bitpool); + sink->stream = g_io_channel_unix_new(ret); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); return 0; } static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, GstCaps *caps, - struct ipc_packet *pkt) + sbc_capabilities_t *pkt) { - - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; + sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities; const GValue *value = NULL; const char *pref, *name; - GstStructure *structure = gst_caps_get_structure(caps,0); + GstStructure *structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name(structure); /* FIXME only sbc supported here, should suport mp3 */ @@ -330,26 +242,21 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, return FALSE; } - if (sink->device) - strncpy(pkt->device, sink->device, 18); - - pkt->role = PKT_ROLE_HIFI; - value = gst_structure_get_value(structure, "rate"); - cfg->rate = g_value_get_int(value); + cfg->frequency = g_value_get_int(value); value = gst_structure_get_value(structure, "mode"); pref = g_value_get_string(value); if (strcmp(pref, "auto") == 0) - cfg->mode = CFG_MODE_AUTO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; else if (strcmp(pref, "mono") == 0) - cfg->mode = CFG_MODE_MONO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; else if (strcmp(pref, "dual") == 0) - cfg->mode = CFG_MODE_DUAL_CHANNEL; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; else if (strcmp(pref, "stereo") == 0) - cfg->mode = CFG_MODE_STEREO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; else if (strcmp(pref, "joint") == 0) - cfg->mode = CFG_MODE_JOINT_STEREO; + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; else { GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); return FALSE; @@ -358,107 +265,27 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, value = gst_structure_get_value(structure, "allocation"); pref = g_value_get_string(value); if (strcmp(pref, "auto") == 0) - sbc->allocation = CFG_ALLOCATION_AUTO; + cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; else if (strcmp(pref, "loudness") == 0) - sbc->allocation = CFG_ALLOCATION_LOUDNESS; + cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; else if (strcmp(pref, "snr") == 0) - sbc->allocation = CFG_ALLOCATION_SNR; + cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; else { GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); return FALSE; } value = gst_structure_get_value(structure, "subbands"); - sbc->subbands = g_value_get_int(value); + cfg->subbands = g_value_get_int(value); value = gst_structure_get_value(structure, "blocks"); - sbc->blocks = g_value_get_int(value); + cfg->block_length = g_value_get_int(value); + /* FIXME min and max ??? */ value = gst_structure_get_value(structure, "bitpool"); - sbc->bitpool = g_value_get_int(value); - - pkt->length = sizeof(*cfg) + sizeof(*sbc); - pkt->type = PKT_TYPE_CFG_REQ; - pkt->error = PKT_ERROR_NONE; - - return TRUE; -} - -static gboolean gst_a2dp_sink_conf_resp(GstA2dpSink *sink) -{ - gchar buf[IPC_MTU]; - GIOError io_error; - gsize ret; - gint total; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - memset(buf, 0, sizeof(buf)); - - io_error = g_io_channel_read(sink->server, (gchar *) buf, - sizeof(*pkt) + sizeof(*cfg), &ret); - if (io_error != G_IO_ERROR_NONE && ret > 0) { - GST_ERROR_OBJECT(sink, "Error ocurred while receiving " - "configurarion packet answer"); - return FALSE; - } - - total = ret; - if (pkt->type != PKT_TYPE_CFG_RSP) { - GST_ERROR_OBJECT(sink, "Unexpected packet type %d " - "received", pkt->type); - return FALSE; - } - - if (pkt->error != PKT_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error %d while configuring " - "device", pkt->error); - return FALSE; - } - - if (cfg->codec != CFG_CODEC_SBC) { - GST_ERROR_OBJECT(sink, "Unsupported format"); - return FALSE; - } - - io_error = g_io_channel_read(sink->server, (gchar *) sbc, - sizeof(*sbc), &ret); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error while reading data from socket " - "%s (%d)", strerror(errno), errno); - return FALSE; - } else if (ret == 0) { - GST_ERROR_OBJECT(sink, "Read 0 bytes from socket"); - return FALSE; - } - - total += ret; - GST_DEBUG_OBJECT(sink, "OK - %d bytes received", total); - - if (pkt->length != (total - sizeof(struct ipc_packet))) { - GST_ERROR_OBJECT(sink, "Error while configuring device: " - "packet size doesn't match"); - return FALSE; - } - - memcpy(&sink->data->cfg, cfg, sizeof(*cfg)); - memcpy(sink->sbc, sbc, sizeof(struct ipc_codec_sbc)); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - gst_a2dp_sink_check_dev_caps(sink); - - GST_DEBUG_OBJECT(sink, "Device configuration:\n\tchannel=%p\n\t" - "fd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - sink->stream, sink->data->cfg.fd_opt, - sink->data->cfg.pkt_len, sink->data->cfg.sample_size, - sink->data->cfg.rate); - - if (sink->data->cfg.codec == CFG_CODEC_SBC) { - /* FIXME is this necessary? */ - ret = gst_a2dp_sink_bluetooth_a2dp_init(sink, sbc); - if (ret < 0) - return FALSE; - } + memcpy(pkt, cfg, sizeof(*pkt)); return TRUE; } @@ -504,7 +331,7 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) GST_LOG_OBJECT(self, "emptying stream pipe"); while (1) { err = g_io_channel_read(self->stream, data->buffer, - (gsize) data->cfg.pkt_len, + (gsize) data->cfg.link_mtu, &read); if (err != G_IO_ERROR_NONE || read <= 0) break; @@ -531,33 +358,6 @@ static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) return TRUE; } -static void gst_a2dp_sink_conf_recv_data(GstA2dpSink *sink) -{ - /* - * We hold the lock, since we can send a signal. - * It is a good practice, according to the glib api. - */ - GST_A2DP_SINK_MUTEX_LOCK(sink); - - switch (sink->con_state) { - case CONFIGURING_SENT_CONF: - if (gst_a2dp_sink_conf_resp(sink)) - sink->con_state = CONFIGURING_RCVD_DEV_CONF; - else - GST_A2DP_SINK_CONFIGURATION_FAIL(sink); - break; - case CONFIGURING_RCVD_DEV_CONF: - if (gst_a2dp_sink_conf_recv_stream_fd(sink)) - GST_A2DP_SINK_CONFIGURATION_SUCCESS(sink); - else - GST_A2DP_SINK_CONFIGURATION_FAIL(sink); - break; - } - - GST_A2DP_SINK_MUTEX_UNLOCK(sink); -} - - static gboolean server_callback(GIOChannel *chan, GIOCondition cond, gpointer data) { @@ -568,16 +368,217 @@ static gboolean server_callback(GIOChannel *chan, else if (cond & G_IO_ERR) { sink = GST_A2DP_SINK(data); GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - } else if (cond & G_IO_IN) { - sink = GST_A2DP_SINK(data); - if (sink->con_state != NOT_CONFIGURED && - sink->con_state != CONFIGURED) - gst_a2dp_sink_conf_recv_data(sink); - else - GST_WARNING_OBJECT(sink, "Unexpected data received"); + } + + return TRUE; +} + +static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) +{ + sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities; + GstStructure *structure; + GValue *value; + GValue *list; + gchar *tmp; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_structure_empty_new("audio/x-sbc"); + value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + + /* mode */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); } else { - sink = GST_A2DP_SINK(data); - GST_WARNING_OBJECT(sink, "Unexpected callback call"); + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "mode", list); + g_free(list); + list = NULL; + } + + /* subbands */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + value = g_value_init(value, G_TYPE_INT); + if (sbc->subbands & BT_A2DP_SUBBANDS_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + if (sbc->subbands & BT_A2DP_SUBBANDS_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "subbands", list); + g_free(list); + list = NULL; + } + + /* blocks */ + value = g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { + g_value_set_int(value, 16); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { + g_value_set_int(value, 12); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "blocks", list); + g_free(list); + list = NULL; + } + + /* allocation */ + g_value_init(value, G_TYPE_STRING); + list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); + if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "allocation", list); + g_free(list); + list = NULL; + } + + /* rate */ + g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* bitpool */ + value = g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool); + gst_structure_set_value(structure, "bitpool", value); + + /* channels */ + gst_value_set_int_range(value, 1, 2); + gst_structure_set_value(structure, "channels", value); + + g_free(value); + + self->dev_caps = gst_caps_new_full(structure, NULL); + + tmp = gst_caps_to_string(self->dev_caps); + GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); + g_free(tmp); + + return TRUE; +} + +static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) +{ + gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *req = (void *) buf; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + GIOError io_error; + + memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + + req->h.msg_type = BT_GETCAPABILITIES_REQ; + strncpy(req->device, self->device, 18); + + req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while asking device caps"); + } + + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_GETCAPABILITIES_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while getting device caps"); + return FALSE; + } + + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { + GST_ERROR_OBJECT(self, "Non a2dp answer from device"); + return FALSE; + } + + memcpy(&self->data->cfg, rsp, sizeof(*rsp)); + if (!gst_a2dp_sink_update_caps(self)) { + GST_WARNING_OBJECT(self, "failed to update capabilities"); + return FALSE; } return TRUE; @@ -586,7 +587,6 @@ static gboolean server_callback(GIOChannel *chan, static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) { GstA2dpSink *self = GST_A2DP_SINK(basesink); - struct sockaddr_un addr = { AF_UNIX, IPC_SOCKET_NAME }; gint sk; gint err; @@ -594,107 +594,146 @@ static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) self->watch_id = 0; - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { + sk = bt_audio_service_open(); + if (sk <= 0) { err = errno; - GST_ERROR_OBJECT(self, "Cannot open socket: %s (%d)", - strerror(err), err); - return FALSE; - } - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = errno; - GST_ERROR_OBJECT(self, "Connection fail %s (%d)", - strerror(err), err); - close(sk); - return FALSE; + GST_ERROR_OBJECT(self, "Cannot open connection to bt " + "audio service: %s %d", strerror(err), err); + goto failed; } self->server = g_io_channel_unix_new(sk); - - self->watch_id = g_io_add_watch(self->server, G_IO_IN | G_IO_HUP | - G_IO_ERR | G_IO_NVAL, server_callback, self); + self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | + G_IO_NVAL, server_callback, self); self->data = g_new0(struct bluetooth_data, 1); memset(self->data, 0, sizeof(struct bluetooth_data)); - self->sbc = g_new0(struct ipc_codec_sbc, 1); - self->stream = NULL; - self->con_state = NOT_CONFIGURED; - self->new_dev_caps = FALSE; - self->dev_caps = NULL; + self->stream_caps = NULL; - self->waiting_con_conf = FALSE; + if (!gst_a2dp_sink_get_capabilities(self)) + goto failed; return TRUE; + +failed: + bt_audio_service_close(sk); + return FALSE; } -static gboolean gst_a2dp_sink_send_conf_pkt(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) { - gchar buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - gboolean ret; - gsize bytes_sent; + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *req = (void *) buf; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_datafd_ind *ind = (void*) buf; GIOError io_error; - g_assert(sink->con_state == NOT_CONFIGURED); + GST_DEBUG_OBJECT(self, "stream start"); - memset (pkt, 0, sizeof(buf)); - ret = gst_a2dp_sink_init_pkt_conf(sink, caps, pkt); - if (!ret) { - GST_ERROR_OBJECT(sink, "Couldn't initialize parse caps " - "to packet configuration"); + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_STREAMSTART_REQ; + + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "start packet"); return FALSE; } - sink->con_state = CONFIGURING_INIT; + GST_DEBUG_OBJECT(self, "stream start packet sent"); - io_error = g_io_channel_write(sink->server, (gchar *) pkt, - sizeof(*pkt) + pkt->length, &bytes_sent); + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_STREAMSTART_RSP); if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(sink, "Error ocurred while sending " - "configurarion packet"); - sink->con_state = NOT_CONFIGURED; + GST_ERROR_OBJECT(self, "Error while stream start confirmation"); + return FALSE; + } + + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream started"); + + io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h, + BT_STREAMFD_IND); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving stream fd"); return FALSE; } - GST_DEBUG_OBJECT(sink, "%d bytes sent", bytes_sent); - sink->con_state = CONFIGURING_SENT_CONF; + if (!gst_a2dp_sink_conf_recv_stream_fd(self)) + return FALSE; return TRUE; } -static gboolean gst_a2dp_sink_start_dev_conf(GstA2dpSink *sink, GstCaps *caps) +static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) { + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *req = (void *) buf; + struct bt_setconfiguration_rsp *rsp = (void *) buf; gboolean ret; + GIOError io_error; + + GST_DEBUG_OBJECT(self, "configuring device"); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_SETCONFIGURATION_REQ; + strncpy(req->device, self->device, 18); + ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); + if (!ret) { + GST_ERROR_OBJECT(self, "Couldn't parse caps " + "to packet configuration"); + return FALSE; + } - g_assert(sink->con_state == NOT_CONFIGURED); + io_error = gst_a2dp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "configurarion packet"); + return FALSE; + } - GST_DEBUG_OBJECT(sink, "starting device configuration"); + GST_DEBUG_OBJECT(self, "configuration packet sent"); - ret = gst_a2dp_sink_send_conf_pkt(sink, caps); + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + BT_SETCONFIGURATION_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); + return FALSE; + } - return ret; + if (rsp->posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", + strerror(rsp->posix_errno), + rsp->posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "configuration set"); + + return TRUE; } static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, GstBuffer *buffer) { GstA2dpSink *sink = GST_A2DP_SINK(basesink); + gboolean ret; GST_A2DP_SINK_MUTEX_LOCK(sink); - if (sink->con_state == NOT_CONFIGURED) - gst_a2dp_sink_start_dev_conf(sink, GST_BUFFER_CAPS(buffer)); - - /* wait for the connection process to finish */ - if (sink->con_state != CONFIGURED) - GST_A2DP_SINK_WAIT_CON_END(sink); + ret = gst_a2dp_sink_stream_start(sink); GST_A2DP_SINK_MUTEX_UNLOCK(sink); - if (sink->con_state != CONFIGURED) + if (!ret) return GST_FLOW_ERROR; return GST_FLOW_OK; @@ -745,7 +784,7 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, encoded = GST_BUFFER_SIZE(buffer); - if (data->count + encoded >= data->cfg.pkt_len) { + if (data->count + encoded >= data->cfg.link_mtu) { ret = gst_a2dp_sink_avdtp_write(self); if (ret < 0) return GST_FLOW_ERROR; @@ -758,24 +797,28 @@ static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, return GST_FLOW_OK; } +static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) +{ + GstA2dpSink *self = GST_A2DP_SINK(basesink); + + return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL; +} + static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) { GstA2dpSink *self = GST_A2DP_SINK(basesink); + gboolean ret; GST_A2DP_SINK_MUTEX_LOCK(self); - if (self->con_state == NOT_CONFIGURED) { - gst_a2dp_sink_start_dev_conf(self, caps); + ret = gst_a2dp_sink_configure(self, caps); - if (self->dev_caps) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_ref(caps); + if (self->stream_caps) + gst_caps_unref(self->stream_caps); + self->stream_caps = gst_caps_ref(caps); - /* we suppose the device will accept this caps */ - self->new_dev_caps = FALSE; - } GST_A2DP_SINK_MUTEX_UNLOCK(self); - return TRUE; + return ret; } static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) @@ -800,12 +843,7 @@ static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink, return GST_FLOW_ERROR; } - if (self->new_dev_caps && self->dev_caps) { - GST_INFO_OBJECT(self, "new caps from device"); - gst_buffer_set_caps(*buf, self->dev_caps); - self->new_dev_caps = FALSE; - } else - gst_buffer_set_caps(*buf, caps); + gst_buffer_set_caps(*buf, caps); GST_BUFFER_OFFSET(*buf) = offset; @@ -830,6 +868,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render); basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); + basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps); basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc); @@ -847,12 +886,79 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { self->device = NULL; self->data = NULL; - self->sbc = NULL; self->stream = NULL; - self->con_state = NOT_CONFIGURED; - self->con_conf_end = g_cond_new(); - self->waiting_con_conf = FALSE; + self->dev_caps = NULL; + self->sink_lock = g_mutex_new(); } + +static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, + const bt_audio_msg_header_t *msg) +{ + gint err; + GIOError error; + gsize written; + + GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type)); + + error = g_io_channel_write(self->server, (const gchar*) msg, + BT_AUDIO_IPC_PACKET_SIZE, &written); + if (error != G_IO_ERROR_NONE) { + err = errno; + GST_ERROR_OBJECT(self, "Error sending data to audio service:" + " %s(%d)", strerror(err), err); + } + + return error; +} + +static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self, + bt_audio_msg_header_t *inmsg) +{ + GIOError status; + gsize bytes_read; + const char *type; + + status = g_io_channel_read(self->server, (gchar*) inmsg, + BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); + if (status != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error receiving data from service"); + return status; + } + + type = bt_audio_strmsg(inmsg->msg_type); + if (!type) { + GST_ERROR_OBJECT(self, "Bogus message type %d " + "received from audio service", + inmsg->msg_type); + return G_IO_ERROR_INVAL; + } + + GST_DEBUG_OBJECT(self, "Received %s", type); + + return status; +} + +static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type) +{ + GIOError status; + + status = gst_a2dp_sink_audioservice_recv(self, outmsg); + if (status != G_IO_ERROR_NONE) + return status; + + if (outmsg->msg_type != expected_type) { + GST_ERROR_OBJECT(self, "Bogus message %s " + "received while %s was expected", + bt_audio_strmsg(outmsg->msg_type), + bt_audio_strmsg(expected_type)); + return G_IO_ERROR_INVAL; + } + + return status; +} + diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index ea750406..f5b9b69b 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -49,15 +49,13 @@ struct _GstA2dpSink { GIOChannel *stream; struct bluetooth_data *data; - struct ipc_codec_sbc *sbc; GIOChannel *server; - gint con_state; + /* stream connection data */ + GstCaps *stream_caps; + GstCaps *dev_caps; - gboolean new_dev_caps; - GCond *con_conf_end; - gboolean waiting_con_conf; GMutex *sink_lock; guint watch_id; diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 54bb7f8a..021ecacf 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -27,14 +27,17 @@ #include +#include "ipc.h" #include "gstsbcenc.h" #include "gstsbcutil.h" -#define SBC_ENC_DEFAULT_MODE CFG_MODE_AUTO -#define SBC_ENC_DEFAULT_BLOCKS 16 -#define SBC_ENC_DEFAULT_SUB_BANDS 8 -#define SBC_ENC_DEFAULT_BITPOOL 53 -#define SBC_ENC_DEFAULT_ALLOCATION CFG_ALLOCATION_AUTO +#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_AUTO +#define SBC_ENC_DEFAULT_BLOCKS 0 +#define SBC_ENC_DEFAULT_SUB_BANDS 0 +#define SBC_ENC_DEFAULT_BITPOOL 0 +#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_AUTO +#define SBC_ENC_DEFAULT_RATE 0 +#define SBC_ENC_DEFAULT_CHANNELS 0 GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug @@ -65,9 +68,9 @@ static GType gst_sbc_allocation_get_type(void) { static GType sbc_allocation_type = 0; static GEnumValue sbc_allocations[] = { - { CFG_ALLOCATION_AUTO, "Auto", "auto" }, - { CFG_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, - { CFG_ALLOCATION_SNR, "SNR", "snr" }, + { BT_A2DP_ALLOCATION_AUTO, "Auto", "auto" }, + { BT_A2DP_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, + { BT_A2DP_ALLOCATION_SNR, "SNR", "snr" }, { -1, NULL, NULL} }; @@ -115,73 +118,193 @@ static GstStaticPadTemplate sbc_enc_src_factory = "allocation = (string) { snr, loudness }," "bitpool = (int) [ 2, 64 ]")); -static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc, GstCaps *caps) +gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); + +static void sbc_enc_set_structure_int_param(GstSbcEnc *enc, + GstStructure *structure, const gchar* field, + gint field_value) +{ + GValue *value; + + value = g_new0(GValue,1); + value = g_value_init(value, G_TYPE_INT); + g_value_set_int(value, field_value); + gst_structure_set_value(structure, field, value); + g_free(value); +} + +static void sbc_enc_set_structure_string_param(GstSbcEnc *enc, + GstStructure *structure, const gchar* field, + const gchar* field_value) +{ + GValue *value; + + value = g_new0(GValue,1); + value = g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, field_value); + gst_structure_set_value(structure, field, value); + g_free(value); +} + +static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) { - gint rate; - gint channels; GstCaps* src_caps; GstStructure *structure; - const gchar *mode; - const gchar *allocation; + GEnumValue *enum_value; + GEnumClass *enum_class; + gchar* temp; + + src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); + structure = gst_caps_get_structure(src_caps, 0); + + if (enc->rate != 0) + sbc_enc_set_structure_int_param(enc, structure, "rate", + enc->rate); + + if (enc->channels != 0) + sbc_enc_set_structure_int_param(enc, structure, "channels", + enc->channels); + + if (enc->subbands != 0) + sbc_enc_set_structure_int_param(enc, structure, "subbands", + enc->subbands); + + if (enc->blocks != 0) + sbc_enc_set_structure_int_param(enc, structure, "blocks", + enc->blocks); + + if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { + enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); + enum_value = g_enum_get_value(enum_class, enc->mode); + sbc_enc_set_structure_string_param(enc, structure, "mode", + enum_value->value_nick); + g_type_class_unref(enum_class); + } - structure = gst_caps_get_structure(caps, 0); + if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) { + enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); + enum_value = g_enum_get_value(enum_class, enc->allocation); + sbc_enc_set_structure_string_param(enc, structure, "allocation", + enum_value->value_nick); + g_type_class_unref(enum_class); + } - if (!gst_structure_get_int (structure, "rate", &rate)) - return NULL; - if (!gst_structure_get_int (structure, "channels", &channels)) + temp = gst_caps_to_string(src_caps); + GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp); + g_free(temp); + + return src_caps; +} + +static GstCaps* sbc_enc_src_getcaps (GstPad * pad) +{ + GstSbcEnc *enc; + + enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); + + return sbc_enc_generate_srcpad_caps(enc); +} + +static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps) +{ + GstCaps* srcpad_caps; + GstCaps* temp_caps; + gboolean res = TRUE; + GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); + + GST_LOG_OBJECT(enc, "setting srcpad caps"); + + srcpad_caps = sbc_enc_generate_srcpad_caps(enc); + temp_caps = gst_caps_intersect(srcpad_caps, caps); + if (temp_caps == GST_CAPS_NONE) + res = FALSE; + + gst_caps_unref(temp_caps); + gst_caps_unref(srcpad_caps); + + g_return_val_if_fail(res, FALSE); + + return gst_sbc_enc_fill_sbc_params(enc, caps); +} + +static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) +{ + + gchar *error_message = NULL; + GstCaps* result; + + result = gst_sbc_util_caps_fixate(caps, &error_message); + + if (!result) { + GST_ERROR_OBJECT (enc, "Invalid input caps caused parsing " + "error: %s", error_message); + g_free(error_message); return NULL; + } - enc->sbc.rate = rate; - enc->sbc.channels = channels; + return result; +} - if (enc->mode == CFG_MODE_AUTO) - enc->mode = CFG_MODE_JOINT_STEREO; +static GstCaps* sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc) +{ + GstCaps *peer_caps; + GstCaps *src_caps; + GstCaps *caps; + gboolean res = TRUE; + GstCaps *result_caps = NULL; + + peer_caps = gst_pad_peer_get_caps(enc->srcpad); + if (!peer_caps) + return NULL; - if (enc->mode == CFG_MODE_MONO || enc->mode == CFG_MODE_JOINT_STEREO) - enc->sbc.joint = 1; + src_caps = sbc_enc_generate_srcpad_caps(enc); + caps = gst_caps_intersect(src_caps, peer_caps); - enc->sbc.blocks = enc->blocks; - enc->sbc.subbands = enc->subbands; - if (enc->allocation == 0) - enc->sbc.allocation = CFG_ALLOCATION_LOUDNESS; - else - enc->sbc.allocation = enc->allocation; + if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) { + res = FALSE; + goto done; + } - enc->sbc.bitpool = SBC_ENC_DEFAULT_BITPOOL; + result_caps = sbc_enc_src_caps_fixate(enc, caps); - mode = gst_sbc_get_mode_string(enc->sbc.joint); - allocation = gst_sbc_get_allocation_string(enc->sbc.allocation); +done: - src_caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, enc->sbc.rate, - "channels", G_TYPE_INT, enc->sbc.channels, - "mode", G_TYPE_STRING, mode, - "subbands", G_TYPE_INT, enc->sbc.subbands, - "blocks", G_TYPE_INT, enc->sbc.blocks, - "allocation", G_TYPE_STRING, allocation, - "bitpool", G_TYPE_INT, enc->sbc.bitpool, - NULL); + gst_caps_unref(src_caps); + gst_caps_unref(peer_caps); + gst_caps_unref(caps); - return src_caps; + if (!res) + return NULL; + + return result_caps; } static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) { GstSbcEnc *enc; GstStructure *structure; - GstCaps *othercaps; + GstCaps *src_caps; + gint rate, channels; + gboolean res; enc = GST_SBC_ENC(GST_PAD_PARENT (pad)); structure = gst_caps_get_structure(caps, 0); - othercaps = sbc_enc_generate_srcpad_caps(enc, caps); - if (othercaps == NULL) + if (!gst_structure_get_int(structure, "rate", &rate)) + goto error; + if (!gst_structure_get_int(structure, "channels", &channels)) goto error; - gst_pad_set_caps (enc->srcpad, othercaps); - gst_caps_unref(othercaps); + enc->rate = rate; + enc->channels = channels; - return TRUE; + src_caps = sbc_enc_get_fixed_srcpad_caps(enc); + if (!src_caps) + goto error; + res = gst_pad_set_caps(enc->srcpad, src_caps); + gst_caps_unref(src_caps); + + return res; error: GST_ERROR_OBJECT (enc, "invalid input caps"); @@ -215,14 +338,13 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) if (!(allocation = gst_structure_get_string(structure, "allocation"))) return FALSE; - sbc_reinit(&enc->sbc, 0); - enc->sbc.rate = rate; - enc->sbc.channels = channels; - enc->blocks = blocks; - enc->sbc.subbands = subbands; + enc->rate = enc->sbc.rate = rate; + enc->channels = enc->sbc.channels = channels; + enc->blocks = enc->sbc.blocks = blocks; + enc->subbands = enc->sbc.subbands = subbands; enc->sbc.bitpool = bitpool; - enc->mode = gst_sbc_get_mode_int(mode); - enc->allocation = gst_sbc_get_allocation_mode_int(allocation); + enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode); + enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); return TRUE; } @@ -291,11 +413,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); - if (res != GST_FLOW_OK) { - GST_ERROR_OBJECT(enc, "pad pushing failed"); + if (res != GST_FLOW_OK) goto done; - } - } done: @@ -350,12 +469,30 @@ static void gst_sbc_enc_base_init(gpointer g_class) gst_element_class_set_details(element_class, &sbc_enc_details); } +static gboolean sbc_enc_set_blocks(GstSbcEnc *enc, gint value) +{ + if (value != 4 && value != 8 && value != 12 && + value != 16 && value != 0) + return FALSE; + enc->blocks = value; + return TRUE; +} + +static gboolean sbc_enc_set_subbands(GstSbcEnc *enc, gint value) +{ + if (value != 4 && value != 8 && value != 0) + return FALSE; + enc->subbands = value; + return TRUE; +} + static void gst_sbc_enc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GstSbcEnc *enc = GST_SBC_ENC(object); - /* TODO - CAN ONLY BE CHANGED ON READY AND BELOW */ + /* changes to those properties will only happen on the next caps + * negotiation */ switch (prop_id) { case PROP_MODE: @@ -365,12 +502,14 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id, enc->allocation = g_value_get_enum(value); break; case PROP_BLOCKS: - /* TODO - verify consistency */ - enc->blocks = g_value_get_int(value); + if (!sbc_enc_set_blocks(enc, g_value_get_int(value))) + GST_WARNING_OBJECT(enc, "invalid value %d for " + "blocks property", g_value_get_int(value)); break; case PROP_SUBBANDS: - /* TODO - verify consistency */ - enc->subbands = g_value_get_int(value); + if (!sbc_enc_set_subbands(enc, g_value_get_int(value))) + GST_WARNING_OBJECT(enc, "invalid value %d for " + "subbands property", g_value_get_int(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -428,7 +567,7 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) g_object_class_install_property(object_class, PROP_BLOCKS, g_param_spec_int("blocks", "Blocks", "Blocks", 0, G_MAXINT, - SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); + SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_SUBBANDS, g_param_spec_int("subbands", "Sub Bands", @@ -447,13 +586,18 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + gst_pad_set_getcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps)); + gst_pad_set_setcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; self->blocks = SBC_ENC_DEFAULT_BLOCKS; self->mode = SBC_ENC_DEFAULT_MODE; self->allocation = SBC_ENC_DEFAULT_ALLOCATION; + self->rate = SBC_ENC_DEFAULT_RATE; + self->channels = SBC_ENC_DEFAULT_CHANNELS; self->adapter = gst_adapter_new(); } diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index c5fc6bcc..f65bcc94 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -25,8 +25,6 @@ #include #include "sbc.h" -#include "ipc.h" - G_BEGIN_DECLS @@ -51,6 +49,8 @@ struct _GstSbcEnc { GstPad *srcpad; GstAdapter *adapter; + gint rate; + gint channels; gint mode; gint blocks; gint allocation; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 185cda03..bae7d623 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -57,6 +57,7 @@ static GstStaticPadTemplate sbc_parse_src_factory = "bitpool = (int) [ 2, 64 ]")); /* Creates a fixed caps from the caps given. */ +/* FIXME use gstsbcutil caps fixating function */ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) { GstCaps *result; @@ -67,6 +68,11 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) const gchar* allocation = NULL; const gchar* mode = NULL; const gchar* error_message = NULL; + gchar* str; + + str = gst_caps_to_string(caps); + GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str); + g_free(str); structure = gst_caps_get_structure(caps, 0); @@ -76,11 +82,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "rate"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_rate_from_list(value); - } else { + else temp = g_value_get_int(value); - } rate = temp; } @@ -90,11 +95,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "channels"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) { + if (GST_VALUE_HOLDS_INT_RANGE(value)) temp = gst_sbc_select_channels_from_range(value); - } else { + else temp = g_value_get_int(value); - } channels = temp; } @@ -104,11 +108,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "blocks"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_blocks_from_list(value); - } else { + else temp = g_value_get_int(value); - } blocks = temp; } @@ -118,11 +121,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "subbands"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) temp = gst_sbc_select_subbands_from_list(value); - } else { + else temp = g_value_get_int(value); - } subbands = temp; } @@ -132,11 +134,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "bitpool"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) { + if (GST_VALUE_HOLDS_INT_RANGE(value)) temp = gst_sbc_select_bitpool_from_range(value); - } else { + else temp = g_value_get_int(value); - } bitpool = temp; } @@ -146,11 +147,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "allocation"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) allocation = gst_sbc_get_allocation_from_list(value); - } else { + else allocation = g_value_get_string(value); - } } if (!gst_structure_has_field(structure, "mode")) { @@ -159,11 +159,10 @@ static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) goto error; } else { value = gst_structure_get_value(structure, "mode"); - if (GST_VALUE_HOLDS_LIST(value)) { + if (GST_VALUE_HOLDS_LIST(value)) mode = gst_sbc_get_mode_from_list(value); - } else { + else mode = g_value_get_string(value); - } } error: @@ -205,9 +204,15 @@ static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) other = gst_caps_new_any(); inter = gst_caps_intersect(caps, other); + if (gst_caps_is_empty(inter)) { + gst_caps_unref(inter); + return FALSE; + } srccaps = sbc_parse_select_caps(parse, inter); - if (srccaps == NULL) + if (srccaps == NULL) { + gst_caps_unref(inter); return FALSE; + } gst_pad_set_caps(parse->srcpad, srccaps); diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 1d7a1227..f2351e6b 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -99,11 +99,11 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value) gint gst_sbc_get_allocation_mode_int(const gchar *allocation) { if (g_ascii_strcasecmp(allocation, "loudness") == 0) - return CFG_ALLOCATION_LOUDNESS; + return BT_A2DP_ALLOCATION_LOUDNESS; else if (g_ascii_strcasecmp(allocation, "snr") == 0) - return CFG_ALLOCATION_SNR; + return BT_A2DP_ALLOCATION_SNR; else if (g_ascii_strcasecmp(allocation, "auto") == 0) - return CFG_ALLOCATION_AUTO; + return BT_A2DP_ALLOCATION_AUTO; else return -1; } @@ -111,15 +111,15 @@ gint gst_sbc_get_allocation_mode_int(const gchar *allocation) gint gst_sbc_get_mode_int(const gchar *mode) { if (g_ascii_strcasecmp(mode, "joint") == 0) - return CFG_MODE_JOINT_STEREO; + return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; else if (g_ascii_strcasecmp(mode, "stereo") == 0) - return CFG_MODE_STEREO; + return BT_A2DP_CHANNEL_MODE_STEREO; else if (g_ascii_strcasecmp(mode, "dual") == 0) - return CFG_MODE_DUAL_CHANNEL; + return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; else if (g_ascii_strcasecmp(mode, "mono") == 0) - return CFG_MODE_MONO; + return BT_A2DP_CHANNEL_MODE_MONO; else if (g_ascii_strcasecmp(mode, "auto") == 0) - return CFG_MODE_AUTO; + return BT_A2DP_CHANNEL_MODE_AUTO; else return -1; } @@ -127,15 +127,15 @@ gint gst_sbc_get_mode_int(const gchar *mode) const gchar *gst_sbc_get_mode_string(int joint) { switch (joint) { - case CFG_MODE_MONO: + case BT_A2DP_CHANNEL_MODE_MONO: return "mono"; - case CFG_MODE_DUAL_CHANNEL: + case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: return "dual"; - case CFG_MODE_STEREO: + case BT_A2DP_CHANNEL_MODE_STEREO: return "stereo"; - case CFG_MODE_JOINT_STEREO: + case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: return "joint"; - case CFG_MODE_AUTO: + case BT_A2DP_CHANNEL_MODE_AUTO: return NULL; /* TODO what should be selected here? */ default: return NULL; @@ -145,37 +145,165 @@ const gchar *gst_sbc_get_mode_string(int joint) const gchar *gst_sbc_get_allocation_string(int alloc) { switch (alloc) { - case CFG_ALLOCATION_LOUDNESS: + case BT_A2DP_ALLOCATION_LOUDNESS: return "loudness"; - case CFG_ALLOCATION_SNR: + case BT_A2DP_ALLOCATION_SNR: return "snr"; - case CFG_ALLOCATION_AUTO: + case BT_A2DP_ALLOCATION_AUTO: return "loudness"; /* TODO what should be selected here? */ default: return NULL; } } -GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, - struct ipc_codec_sbc *sbc, gint channels) +GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels) { GstCaps *caps; const gchar *mode_str; const gchar *allocation_str; - mode_str = gst_sbc_get_mode_string(cfg->mode); - allocation_str = gst_sbc_get_allocation_string(sbc->allocation); + mode_str = gst_sbc_get_mode_string(sbc->channel_mode); + allocation_str = gst_sbc_get_allocation_string(sbc->allocation_method); caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, cfg->rate, + "rate", G_TYPE_INT, sbc->frequency, "channels", G_TYPE_INT, channels, "mode", G_TYPE_STRING, mode_str, "subbands", G_TYPE_INT, sbc->subbands, - "blocks", G_TYPE_INT, sbc->blocks, + "blocks", G_TYPE_INT, sbc->block_length, "allocation", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->bitpool, + "bitpool", G_TYPE_INT, sbc->max_bitpool, NULL); return caps; } +/* + * Given a GstCaps, this will return a fixed GstCaps on sucessfull conversion. + * If an error occurs, it will return NULL and error_message will contain the + * error message. + * + * error_message must be passed NULL, if an error occurs, the caller has the + * ownership of the error_message, it must be freed after use. + */ +GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message) +{ + GstCaps *result; + GstStructure *structure; + const GValue *value; + gboolean error = FALSE; + gint temp, rate, channels, blocks, subbands, bitpool; + const gchar* allocation = NULL; + const gchar* mode = NULL; + + g_assert(*error_message == NULL); + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_has_field(structure, "rate")) { + error = TRUE; + *error_message = g_strdup("no rate"); + goto error; + } else { + value = gst_structure_get_value(structure, "rate"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_rate_from_list(value); + else + temp = g_value_get_int(value); + rate = temp; + } + + if (!gst_structure_has_field(structure, "channels")) { + error = TRUE; + *error_message = g_strdup("no channels"); + goto error; + } else { + value = gst_structure_get_value(structure, "channels"); + if (GST_VALUE_HOLDS_INT_RANGE(value)) + temp = gst_sbc_select_channels_from_range(value); + else + temp = g_value_get_int(value); + channels = temp; + } + + if (!gst_structure_has_field(structure, "blocks")) { + error = TRUE; + *error_message = g_strdup("no blocks."); + goto error; + } else { + value = gst_structure_get_value(structure, "blocks"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_blocks_from_list(value); + else + temp = g_value_get_int(value); + blocks = temp; + } + + if (!gst_structure_has_field(structure, "subbands")) { + error = TRUE; + *error_message = g_strdup("no subbands"); + goto error; + } else { + value = gst_structure_get_value(structure, "subbands"); + if (GST_VALUE_HOLDS_LIST(value)) + temp = gst_sbc_select_subbands_from_list(value); + else + temp = g_value_get_int(value); + subbands = temp; + } + + if (!gst_structure_has_field(structure, "bitpool")) { + error = TRUE; + *error_message = g_strdup("no bitpool"); + goto error; + } else { + value = gst_structure_get_value(structure, "bitpool"); + if (GST_VALUE_HOLDS_INT_RANGE(value)) + temp = gst_sbc_select_bitpool_from_range(value); + else + temp = g_value_get_int(value); + bitpool = temp; + } + + if (!gst_structure_has_field(structure, "allocation")) { + error = TRUE; + *error_message = g_strdup("no allocation"); + goto error; + } else { + value = gst_structure_get_value(structure, "allocation"); + if (GST_VALUE_HOLDS_LIST(value)) + allocation = gst_sbc_get_allocation_from_list(value); + else + allocation = g_value_get_string(value); + } + + if (!gst_structure_has_field(structure, "mode")) { + error = TRUE; + *error_message = g_strdup("no mode"); + goto error; + } else { + value = gst_structure_get_value(structure, "mode"); + if (GST_VALUE_HOLDS_LIST(value)) + mode = gst_sbc_get_mode_from_list(value); + else + mode = g_value_get_string(value); + } + +error: + if (error) + return NULL; + + result = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, rate, + "channels", G_TYPE_INT, channels, + "mode", G_TYPE_STRING, mode, + "blocks", G_TYPE_INT, blocks, + "subbands", G_TYPE_INT, subbands, + "allocation", G_TYPE_STRING, allocation, + "bitpool", G_TYPE_INT, bitpool, + NULL); + + return result; +} + + diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 0c91fe82..4581abf7 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -22,10 +22,9 @@ */ #include -#include "sbc.h" -struct ipc_data_cfg; /* FIXME can't include ipc.h */ -struct ipc_codec_sbc; +#include "sbc.h" +#include "ipc.h" gint gst_sbc_select_rate_from_list(const GValue *value); @@ -47,6 +46,6 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *value); gint gst_sbc_get_mode_int(const gchar *mode); const gchar *gst_sbc_get_mode_string(int joint); -GstCaps* gst_sbc_caps_from_sbc(struct ipc_data_cfg *cfg, struct ipc_codec_sbc *sbc, - gint channels); +GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); +GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); diff --git a/audio/ipc.c b/audio/ipc.c new file mode 100644 index 00000000..05920648 --- /dev/null +++ b/audio/ipc.c @@ -0,0 +1,119 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ipc.h" + +/* This table contains the string representation for messages */ +static const char *strmsg[] = { + "BT_GETCAPABILITIES_REQ", + "BT_GETCAPABILITIES_RSP", + "BT_SETCONFIGURATION_REQ", + "BT_SETCONFIGURATION_RSP", + "BT_STREAMSTART_REQ", + "BT_STREAMSTART_RSP", + "BT_STREAMSTOP_REQ", + "BT_STREAMSTOP_RSP", + "BT_STREAMSUSPEND_IND", + "BT_STREAMRESUME_IND", + "BT_CONTROL_REQ", + "BT_CONTROL_RSP", + "BT_CONTROL_IND", + "BT_STREAMFD_IND", +}; + +int bt_audio_service_open() +{ + int sk; + int err; + struct sockaddr_un addr = { + AF_UNIX, BT_IPC_SOCKET_NAME + }; + + sk = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sk < 0) { + err = errno; + fprintf(stderr, "%s: Cannot open socket: %s (%d)\n", + __FUNCTION__, strerror(err), err); + errno = err; + return -1; + } + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + fprintf(stderr, "%s: connect() failed: %s (%d)\n", + __FUNCTION__, strerror(err), err); + close(sk); + errno = err; + return -1; + } + + return sk; +} + +int bt_audio_service_close(int sk) +{ + return close(sk); +} + +int bt_audio_service_get_data_fd(int sk) +{ + char cmsg_b[CMSG_SPACE(sizeof(int))], m; + int err, ret; + struct iovec iov = { &m, sizeof(m) }; + struct msghdr msgh; + struct cmsghdr *cmsg; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(sizeof(int)); + + ret = recvmsg(sk, &msgh, 0); + if (ret < 0) { + err = errno; + fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n", + __FUNCTION__, strerror(err), err); + errno = err; + return -1; + } + + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) + return (*(int *) CMSG_DATA(cmsg)); + } + + errno = EINVAL; + return -1; +} + +const char *bt_audio_strmsg(int type) +{ + if (type < 0 || type > (sizeof(strmsg) / sizeof(strmsg[0]))) + return NULL; + + return strmsg[type]; +} + diff --git a/audio/ipc.h b/audio/ipc.h index 1c26e304..0384cfd6 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -4,132 +4,285 @@ * * Copyright (C) 2004-2007 Marcel Holtmann * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * 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, + * This library 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ -#include +/* + Message sequence chart of streaming sequence for A2DP transport + + Audio daemon User + on snd_pcm_open + <--BT_GETCAPABILITIES_REQ + + BT_GETCAPABILITIES_RSP--> + + on snd_pcm_hw_params + <--BT_SETCONFIGURATION_REQ + + BT_SETCONFIGURATION_RSP--> + + on snd_pcm_prepare + <--BT_STREAMSTART_REQ + + + BT_STREAMSTART_RSP--> -#define IPC_TYPE_CONNECT 0x0001 + BT_STREAMFD_IND --> -#define IPC_MTU 128 + < streams data > + .......... -#define IPC_SOCKET_NAME "\0/org/bluez/audio" + on snd_pcm_drop/snd_pcm_drain -#ifndef UNIX_PATH_MAX -#define UNIX_PATH_MAX 108 + <--BT_STREAMSTOP_REQ + + + BT_STREAMSTOP_RSP--> + + on IPC close or appl crash + + + */ + +#ifndef BT_AUDIOCLIENT_H +#define BT_AUDIOCLIENT_H + +#ifdef __cplusplus +extern "C" { #endif -/* Supported roles */ -#define PKT_ROLE_AUTO 0 -#define PKT_ROLE_VOICE 1 -#define PKT_ROLE_HIFI 2 - -/* Packet types */ -#define PKT_TYPE_CFG_REQ 0 -#define PKT_TYPE_CFG_RSP 1 -#define PKT_TYPE_STATE_REQ 2 -#define PKT_TYPE_STATE_RSP 3 -#define PKT_TYPE_CTL_REQ 4 -#define PKT_TYPE_CTL_RSP 5 -#define PKT_TYPE_CTL_NTFY 6 - -/* Errors codes */ -#define PKT_ERROR_NONE 0 - -struct ipc_packet { - char device[18]; /* Address of the remote Device */ - uint8_t role; /* Audio role eg: voice, wifi, auto... */ - uint8_t type; /* Packet type */ - uint8_t error; /* Packet error code */ - uint8_t length; /* Payload length in bytes */ - uint8_t data[0]; /* Packet payload */ -} __attribute__ ((packed)); +#include +#include +#include +#include +#include +#include + +#define BT_AUDIO_IPC_PACKET_SIZE 128 +#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio" -/* File descriptor options */ -#define CFG_FD_OPT_READ 0 -#define CFG_FD_OPT_WRITE 1 -#define CFG_FD_OPT_READWRITE 2 - -/* Audio channel mode */ -#define CFG_MODE_AUTO 0 -#define CFG_MODE_MONO 1 -#define CFG_MODE_DUAL_CHANNEL 2 -#define CFG_MODE_STEREO 3 -#define CFG_MODE_JOINT_STEREO 4 - -/* Allocation method */ -#define CFG_ALLOCATION_AUTO 0 -#define CFG_ALLOCATION_LOUDNESS 1 -#define CFG_ALLOCATION_SNR 2 - -/* Codec options */ -#define CFG_CODEC_NONE 0 -#define CFG_CODEC_SCO 1 -#define CFG_CODEC_SBC 2 - -struct ipc_data_cfg { - uint8_t fd_opt; /* Stream file descriptor options: read, - write or readwrite */ - uint16_t pkt_len; /* Stream packet length */ - uint8_t sample_size; /* Sample size in bytes */ - uint8_t mode; /* Audio channel mode */ - uint16_t rate; /* Stream sample rate */ - uint8_t codec; /* Stream codec */ - uint8_t data[0]; /* Codec payload */ +/* Generic message header definition */ +typedef struct { + uint8_t msg_type; +} __attribute__ ((packed)) bt_audio_msg_header_t; + +/* Messages list */ +#define BT_GETCAPABILITIES_REQ 0 +#define BT_GETCAPABILITIES_RSP 1 + +#define BT_SETCONFIGURATION_REQ 2 +#define BT_SETCONFIGURATION_RSP 3 + +#define BT_STREAMSTART_REQ 4 +#define BT_STREAMSTART_RSP 5 + +#define BT_STREAMSTOP_REQ 6 +#define BT_STREAMSTOP_RSP 7 + +#define BT_STREAMSUSPEND_IND 8 +#define BT_STREAMRESUME_IND 9 + +#define BT_CONTROL_REQ 10 +#define BT_CONTROL_RSP 11 +#define BT_CONTROL_IND 12 + +#define BT_STREAMFD_IND 13 + +/* BT_GETCAPABILITIES_REQ */ + +#define BT_CAPABILITIES_TRANSPORT_A2DP 0 +#define BT_CAPABILITIES_TRANSPORT_SCO 1 +#define BT_CAPABILITIES_TRANSPORT_ANY 2 + +#define BT_CAPABILITIES_ACCESS_MODE_READ 1 +#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2 +#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3 + +struct bt_getcapabilities_req { + bt_audio_msg_header_t h; + char device[18]; /* Address of the remote Device */ + uint8_t transport; /* Requested transport */ + uint8_t access_mode; /* Requested access mode */ } __attribute__ ((packed)); -struct ipc_codec_sbc { - uint8_t allocation; +/* BT_GETCAPABILITIES_RSP */ + +/** + * SBC Codec parameters as per A2DP profile 1.0 § 4.3 + */ + +#define BT_A2DP_SAMPLING_FREQ_16000 (1 << 3) +#define BT_A2DP_SAMPLING_FREQ_32000 (1 << 2) +#define BT_A2DP_SAMPLING_FREQ_44100 (1 << 1) +#define BT_A2DP_SAMPLING_FREQ_48000 1 + +#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3) +#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1) +#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1 +#define BT_A2DP_CHANNEL_MODE_AUTO 0 + +#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3) +#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2) +#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1) +#define BT_A2DP_BLOCK_LENGTH_16 1 + +#define BT_A2DP_SUBBANDS_4 (1 << 1) +#define BT_A2DP_SUBBANDS_8 1 + +#define BT_A2DP_ALLOCATION_SNR (1 << 1) +#define BT_A2DP_ALLOCATION_LOUDNESS 1 +#define BT_A2DP_ALLOCATION_AUTO 0 + +typedef struct { + uint8_t channel_mode; + uint8_t frequency; + uint8_t allocation_method; uint8_t subbands; - uint8_t blocks; - uint8_t bitpool; + uint8_t block_length; + uint8_t min_bitpool; + uint8_t max_bitpool; +} __attribute__ ((packed)) sbc_capabilities_t; + +/* To be defined */ +typedef struct { +} __attribute__ ((packed)) mpeg_capabilities_t; + +struct bt_getcapabilities_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; + uint8_t transport; /* Granted transport */ + uint8_t access_mode; /* Granted access mode */ + uint16_t link_mtu; /* Max length that transport supports */ + sbc_capabilities_t sbc_capabilities; /* A2DP only */ + mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ + uint16_t sampling_rate; /* SCO only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_REQ */ +struct bt_setconfiguration_req { + bt_audio_msg_header_t h; + char device[18]; /* Address of the remote Device */ + sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field + and next one must be filled */ + mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ +} __attribute__ ((packed)); + +/* BT_SETCONFIGURATION_RSP */ +struct bt_setconfiguration_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; +} __attribute__ ((packed)); + +/* BT_STREAMSTART_REQ */ +#define BT_STREAM_ACCESS_READ 0 +#define BT_STREAM_ACCESS_WRITE 1 +#define BT_STREAM_ACCESS_READWRITE 2 +struct bt_streamstart_req { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTART_RSP */ +struct bt_streamstart_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; +} __attribute__ ((packed)); + +/* BT_STREAMFD_IND */ +/* This message is followed by one byte of data containing the stream data fd + as ancilliary data */ +struct bt_datafd_ind { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_REQ */ +struct bt_streamstop_req { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); + +/* BT_STREAMSTOP_RSP */ +struct bt_streamstop_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; } __attribute__ ((packed)); -/* Device status */ -#define STATE_DISCONNECTED 0 -#define STATE_CONNECTING 1 -#define STATE_CONNECTED 2 -#define STATE_STREAM_STARTING 3 -#define STATE_STREAMING 4 +/* BT_STREAMSUSPEND_IND */ +struct bt_streamsuspend_ind { + bt_audio_msg_header_t h; +} __attribute__ ((packed)); -struct ipc_data_state { - uint8_t state; /* Stream state */ +/* BT_STREAMRESUME_IND */ +struct bt_streamresume_ind { + bt_audio_msg_header_t h; } __attribute__ ((packed)); -#define CTL_MODE_PLAYBACK 0 -#define CTL_MODE_CAPTURE 1 -#define CTL_MODE_GENERAL 2 - -/* Supported control operations */ -#define CTL_KEY_POWER 0x40 -#define CTL_KEY_VOL_UP 0x41 -#define CTL_KEY_VOL_DOWN 0x42 -#define CTL_KEY_MUTE 0x43 -#define CTL_KEY_PLAY 0x44 -#define CTL_KEY_STOP 0x45 -#define CTL_KEY_PAUSE 0x46 -#define CTL_KEY_RECORD 0x47 -#define CTL_KEY_REWIND 0x48 -#define CTL_KEY_FAST_FORWARD 0x49 -#define CTL_KEY_EJECT 0x4A -#define CTL_KEY_FORWARD 0x4B -#define CTL_KEY_BACKWARD 0x4C - -struct ipc_data_ctl { - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ -} __attribute__ ((packed)); +/* BT_CONTROL_REQ */ + +#define BT_CONTROL_KEY_POWER 0x40 +#define BT_CONTROL_KEY_VOL_UP 0x41 +#define BT_CONTROL_KEY_VOL_DOWN 0x42 +#define BT_CONTROL_KEY_MUTE 0x43 +#define BT_CONTROL_KEY_PLAY 0x44 +#define BT_CONTROL_KEY_STOP 0x45 +#define BT_CONTROL_KEY_PAUSE 0x46 +#define BT_CONTROL_KEY_RECORD 0x47 +#define BT_CONTROL_KEY_REWIND 0x48 +#define BT_CONTROL_KEY_FAST_FORWARD 0x49 +#define BT_CONTROL_KEY_EJECT 0x4A +#define BT_CONTROL_KEY_FORWARD 0x4B +#define BT_CONTROL_KEY_BACKWARD 0x4C + +struct bt_control_req { + bt_audio_msg_header_t h; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_RSP */ +struct bt_control_rsp { + bt_audio_msg_header_t h; + uint8_t posix_errno; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* BT_CONTROL_IND */ +struct bt_control_ind { + bt_audio_msg_header_t h; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ +} __attribute__ ((packed)); + +/* Function declaration */ + +/* Opens a connection to the audio service: return a socket descriptor */ +int bt_audio_service_open(); + +/* Closes a connection to the audio service */ +int bt_audio_service_close(int sk); + +/* Receives stream data file descriptor : must be called after a +BT_STREAMFD_IND message is returned */ +int bt_audio_service_get_data_fd(int sk); + +/* Human readable message type string */ +const char *bt_audio_strmsg(int type); + +#ifdef __cplusplus +} +#endif + +#endif /* BT_AUDIOCLIENT_H */ diff --git a/audio/manager.c b/audio/manager.c index b0cc3340..d80fb962 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1052,7 +1052,7 @@ static void parse_stored_devices(char *key, char *value, void *data) return; str2ba(key, &dst); - device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); + device = manager_find_device(&dst, NULL, FALSE); if (device) return; @@ -1111,7 +1111,7 @@ static void register_devices_stored(const char *adapter) return; str2ba(addr, &dst); - device = manager_find_device(&dst, PKT_ROLE_AUTO, FALSE); + device = manager_find_device(&dst, NULL, FALSE); if (device) { info("Setting %s as default device", addr); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index efe1d2aa..f4a20015 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -76,33 +76,60 @@ #endif struct bluetooth_a2dp { - sbc_t sbc; /* Codec data */ - int codesize; /* SBC codesize */ - int samples; /* Number of encoded samples */ - uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - int count; /* Codec transfer buffer counter */ - - int nsamples; /* Cumulative number of codec samples */ - uint16_t seq_num; /* Cumulative packet sequence */ - int frame_count; /* Current frames in buffer*/ + sbc_capabilities_t sbc_capabilities; + sbc_t sbc; /* Codec data */ + int sbc_initialized; /* Keep track if the encoder is initialized */ + int codesize; /* SBC codesize */ + int samples; /* Number of encoded samples */ + uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ + int count; /* Codec transfer buffer counter */ + + int nsamples; /* Cumulative number of codec samples */ + uint16_t seq_num; /* Cumulative packet sequence */ + int frame_count; /* Current frames in buffer*/ +}; + +struct bluetooth_alsa_config { + char device[18]; /* Address of the remote Device */ + int has_device; + uint8_t transport; /* Requested transport */ + int has_transport; + uint16_t rate; + int has_rate; + uint8_t channel_mode; /* A2DP only */ + int has_channel_mode; + uint8_t allocation_method; /* A2DP only */ + int has_allocation_method; + uint8_t subbands; /* A2DP only */ + int has_subbands; + uint8_t block_length; /* A2DP only */ + int has_block_length; + uint8_t bitpool; /* A2DP only */ + int has_bitpool; }; struct bluetooth_data { snd_pcm_ioplug_t io; + struct bluetooth_alsa_config alsa_config; /* ALSA resource file parameters */ volatile snd_pcm_sframes_t hw_ptr; - struct ipc_data_cfg cfg; /* Bluetooth device config */ - struct pollfd stream; /* Audio stream filedescriptor */ - struct pollfd server; /* Audio daemon filedescriptor */ - uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ - int count; /* Transfer buffer counter */ - struct bluetooth_a2dp a2dp; /* A2DP data */ - - pthread_t hw_thread; /* Makes virtual hw pointer move */ - int pipefd[2]; /* Inter thread communication */ + int transport; /* chosen transport SCO or AD2P */ + int link_mtu; /* MTU for selected transport channel */ + volatile struct pollfd stream; /* Audio stream filedescriptor */ + struct pollfd server; /* Audio daemon filedescriptor */ + uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ + int count; /* Transfer buffer counter */ + struct bluetooth_a2dp a2dp; /* A2DP data */ + + pthread_t hw_thread; /* Makes virtual hw pointer move */ + int pipefd[2]; /* Inter thread communication */ int stopped; sig_atomic_t reset; /* Request XRUN handling */ }; +static int audioservice_send(int sk, const bt_audio_msg_header_t *msg); +static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, + int expected_type); + static int bluetooth_start(snd_pcm_ioplug_t *io) { DBG("bluetooth_start %p", io); @@ -215,62 +242,6 @@ iter_sleep: pthread_exit(NULL); } -#if 0 -static int bluetooth_state_init(struct ipc_packet *pkt, int newstate) -{ - struct ipc_data_state *state = (void *) pkt->data; - - pkt->length = sizeof(*state); - pkt->type = PKT_TYPE_STATE_REQ; - pkt->error = PKT_ERROR_NONE; - state->state = newstate; - - return 0; -} - -static int bluetooth_state(struct bluetooth_data *data, int newstate) -{ - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_state *state = (void *) pkt->data; - int ret; - - memset(buf, 0, sizeof(buf)); - - ret = bluetooth_state_init(pkt, newstate); - if (ret < 0) - return -ret; - - ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - DBG("OK - %d bytes sent. Waiting for response...", ret); - - memset(buf, 0, sizeof(buf)); - - ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*state), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - if (pkt->type != PKT_TYPE_STATE_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); - return -EINVAL; - } - - if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error %d while configuring device", pkt->error); - return -pkt->error; - } - - return 0; -} -#endif - static int bluetooth_playback_start(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; @@ -278,9 +249,6 @@ static int bluetooth_playback_start(snd_pcm_ioplug_t *io) DBG("%p", io); -#if 0 - bluetooth_state(data, STATE_STREAMING); -#endif data->stopped = 0; if (data->hw_thread) @@ -297,9 +265,6 @@ static int bluetooth_playback_stop(snd_pcm_ioplug_t *io) DBG("%p", io); -#if 0 - bluetooth_state(data, STATE_CONNECTED); -#endif data->stopped = 1; return 0; @@ -317,7 +282,7 @@ static void bluetooth_exit(struct bluetooth_data *data) struct bluetooth_a2dp *a2dp = &data->a2dp; if (data->server.fd >= 0) - close(data->server.fd); + bt_audio_service_close(data->server.fd); if (data->stream.fd >= 0) close(data->stream.fd); @@ -327,7 +292,7 @@ static void bluetooth_exit(struct bluetooth_data *data) pthread_join(data->hw_thread, 0); } - if (data->cfg.codec == CFG_CODEC_SBC) + if (a2dp->sbc_initialized) sbc_finish(&a2dp->sbc); if (data->pipefd[0] > 0) @@ -354,12 +319,27 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; char c = 'w'; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *start_req = (void*) buf; + struct bt_streamstart_rsp *start_rsp = (void*) buf; + struct bt_datafd_ind *datafd_ind = (void*) buf; + uint32_t period_count = io->buffer_size / io->period_size; + int opt_name, err; + struct timeval t = { 0, period_count }; DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", io->period_size, io->buffer_size); data->reset = 0; + /* As we're gonna receive messages on the server socket, we have to stop the + hw thread that is polling on it, if any */ + if (data->hw_thread) { + pthread_cancel(data->hw_thread); + pthread_join(data->hw_thread, 0); + data->hw_thread = 0; + } + if (io->stream == SND_PCM_STREAM_PLAYBACK) /* If not null for playback, xmms doesn't display time * correctly */ @@ -369,63 +349,174 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) * If it is, capture won't start */ data->hw_ptr = io->period_size; - /* wake up any client polling at us */ - return write(data->pipefd[1], &c, 1); -} + /* send start */ + memset(start_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + start_req->h.msg_type = BT_STREAMSTART_REQ; -static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params) -{ - struct bluetooth_data *data = io->private_data; - uint32_t period_count = io->buffer_size / io->period_size; - int opt_name, err; + err = audioservice_send(data->server.fd, &start_req->h); + if (err < 0) + return err; + + err = audioservice_expect(data->server.fd, &start_rsp->h, + BT_STREAMSTART_RSP); + if (err < 0) + return err; + + if (start_rsp->posix_errno != 0) { + SNDERR("BT_START failed : %s(%d)", + strerror(start_rsp->posix_errno), + start_rsp->posix_errno); + return -start_rsp->posix_errno; + } + + err = audioservice_expect(data->server.fd, &datafd_ind->h, + BT_STREAMFD_IND); + if (err < 0) + return err; + + if (data->stream.fd >= 0) + close(data->stream.fd); + + data->stream.fd = bt_audio_service_get_data_fd(data->server.fd); + if (data->stream.fd < 0) { + return -errno; + } - DBG("fd=%d period_count=%d", data->stream.fd, period_count); + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + SO_SNDTIMEO : SO_RCVTIMEO; - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, + sizeof(t)) < 0) + return -errno; + } else { + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SCO_TXBUFS : SCO_RXBUFS; - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) - return 0; + return 0; - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? + opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? SO_SNDBUF : SO_RCVBUF; - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, + if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, sizeof(period_count)) == 0) - return 0; - - err = errno; + return 0; - SNDERR("%s (%d)", strerror(err), err); + /* FIXME : handle error codes */ + } - /* FIXME: We should not ignores errors in the future. */ - return 0; + /* wake up any client polling at us */ + return write(data->pipefd[1], &c, 1); } static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { struct bluetooth_data *data = io->private_data; - uint32_t period_count = io->buffer_size / io->period_size; - int opt_name, err; - struct timeval t = { 0, period_count }; + struct bluetooth_a2dp *a2dp = &data->a2dp; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *setconf_req = (void*) buf; + struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; + int err; + sbc_capabilities_t active_capabilities; - DBG("fd=%d period_count=%d", data->stream.fd, period_count); + DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", + io->period_size, io->buffer_size); - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDTIMEO : SO_RCVTIMEO; + /* FIXME: this needs to be really implemented (take into account + real asoundrc settings + ALSA hw settings ) once server side sends us + more than one possible configuration */ + active_capabilities = a2dp->sbc_capabilities; - if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, - sizeof(t)) == 0) - return 0; + memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; + setconf_req->sbc_capabilities = active_capabilities; - err = errno; + err = audioservice_send(data->server.fd, &setconf_req->h); + if (err < 0) + return err; - SNDERR("%s (%d)", strerror(err), err); + err = audioservice_expect(data->server.fd, &setconf_rsp->h, + BT_SETCONFIGURATION_RSP); + if (err < 0) + return err; - return -err; + if (setconf_rsp->posix_errno != 0) { + SNDERR("BT_SETCONFIGURATION failed : %s(%d)", + strerror(setconf_rsp->posix_errno), + setconf_rsp->posix_errno); + return -setconf_rsp->posix_errno; + } + + /* Setup SBC encoder now we agree on parameters */ + if (a2dp->sbc_initialized) + sbc_finish(&a2dp->sbc); + + /* FIXME: init using flags? */ + sbc_init(&a2dp->sbc, 0); + a2dp->sbc_initialized = 1; + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) + a2dp->sbc.rate = 16000; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) + a2dp->sbc.rate = 32000; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) + a2dp->sbc.rate = 44100; + + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) + a2dp->sbc.rate = 48000; + + if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + a2dp->sbc.channels = 1; + else + a2dp->sbc.channels = 2; + + if (active_capabilities.channel_mode & + (BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + a2dp->sbc.joint = 1; + else + a2dp->sbc.joint = 0; + + a2dp->sbc.allocation = active_capabilities.allocation_method + == BT_A2DP_ALLOCATION_SNR ? 0x01 : 0x00; + + switch (active_capabilities.subbands) { + case BT_A2DP_SUBBANDS_4: + a2dp->sbc.subbands = 4; + break; + case BT_A2DP_SUBBANDS_8: + a2dp->sbc.subbands = 8; + break; + } + + switch (active_capabilities.block_length) { + case BT_A2DP_BLOCK_LENGTH_4: + a2dp->sbc.blocks = 4; + break; + case BT_A2DP_BLOCK_LENGTH_8: + a2dp->sbc.blocks = 8; + break; + case BT_A2DP_BLOCK_LENGTH_12: + a2dp->sbc.blocks = 12; + break; + case BT_A2DP_BLOCK_LENGTH_16: + a2dp->sbc.blocks = 16; + break; + } + + a2dp->sbc.bitpool = active_capabilities.max_bitpool; + a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * + a2dp->sbc.channels * 2; + a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + + DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", + a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, + a2dp->sbc.bitpool); + + return 0; } static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, @@ -504,7 +595,6 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_uframes_t frames_to_write, ret; unsigned char *buff; int nrecv, frame_size = 0; @@ -517,7 +607,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, frame_size = areas->step / 8; - nrecv = recv(data->stream.fd, data->buffer, cfg.pkt_len, + nrecv = recv(data->stream.fd, data->buffer, data->link_mtu, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); if (nrecv < 0) { @@ -525,28 +615,28 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, goto done; } - if (nrecv != cfg.pkt_len) { + if (nrecv != data->link_mtu) { ret = -EIO; SNDERR(strerror(-ret)); goto done; } /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + cfg.pkt_len / cfg.sample_size) % + data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) % io->buffer_size; proceed: buff = (unsigned char *) areas->addr + (areas->first + areas->step * offset) / 8; - if ((data->count + size * frame_size) <= cfg.pkt_len) + if ((data->count + size * frame_size) <= data->link_mtu) frames_to_write = size; else - frames_to_write = (cfg.pkt_len - data->count) / frame_size; + frames_to_write = (data->link_mtu - data->count) / frame_size; memcpy(buff, data->buffer + data->count, frame_size * frames_to_write); data->count += (frame_size * frames_to_write); - data->count %= cfg.pkt_len; + data->count %= data->link_mtu; /* Return written frames count */ ret = frames_to_write; @@ -562,7 +652,6 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, snd_pcm_uframes_t size) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_sframes_t ret = 0; snd_pcm_uframes_t frames_to_read; uint8_t *buff; @@ -579,10 +668,10 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, } frame_size = areas->step / 8; - if ((data->count + size * frame_size) <= cfg.pkt_len) + if ((data->count + size * frame_size) <= data->link_mtu) frames_to_read = size; else - frames_to_read = (cfg.pkt_len - data->count) / frame_size; + frames_to_read = (data->link_mtu - data->count) / frame_size; DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); @@ -593,12 +682,12 @@ static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, /* Remember we have some frames in the pipe now */ data->count += frames_to_read * frame_size; - if (data->count != cfg.pkt_len) { + if (data->count != data->link_mtu) { ret = frames_to_read; goto done; } - rsend = send(data->stream.fd, data->buffer, cfg.pkt_len, + rsend = send(data->stream.fd, data->buffer, data->link_mtu, io->nonblock ? MSG_DONTWAIT : 0); if (rsend > 0) { /* Reset count pointer */ @@ -710,7 +799,7 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, frames_to_read = (a2dp->codesize - data->count) / frame_size; DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); - DBG("a2dp.count=%d cfg.pkt_len=%d", a2dp->count, data->cfg.pkt_len); + DBG("a2dp.count=%d data.link_mtu=%d", a2dp->count, data->link_mtu); /* FIXME: If state is not streaming then return */ @@ -747,10 +836,11 @@ static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, written, a2dp->count); /* No space left for another frame then send */ - if (a2dp->count + written >= data->cfg.pkt_len) { + if (a2dp->count + written >= data->link_mtu) { avdtp_write(data); - DBG("sending packet %d, count %d, pkt_len %u", c, - old_count, data->cfg.pkt_len); + DBG("sending packet %d, count %d, link_mtu %u", + a2dp->seq_num, a2dp->count, + data->link_mtu); } ret += frames_to_read; @@ -791,7 +881,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .stop = bluetooth_playback_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, .poll_descriptors = bluetooth_playback_poll_descriptors, @@ -804,7 +893,6 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_read, .poll_descriptors = bluetooth_poll_descriptors, @@ -841,7 +929,6 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, /* Mmap access is really useless fo this driver, but we @@ -852,7 +939,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; - int err, channels; + int err; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -867,21 +954,20 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - channels, channels); + 1, 1); if (err < 0) return err; /* supported rate */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + 8000, 8000); if (err < 0) return err; /* supported block size */ err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - cfg.pkt_len, cfg.pkt_len); + data->link_mtu, data->link_mtu); if (err < 0) return err; @@ -896,7 +982,7 @@ static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; - struct ipc_data_cfg cfg = data->cfg; + struct bluetooth_a2dp *a2dp = &data->a2dp; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, /* Mmap access is really useless fo this driver, but we @@ -907,10 +993,12 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int format_list[] = { SND_PCM_FORMAT_S16_LE }; + unsigned int rate_list[4]; + unsigned int rate_count; + int err, min_channels, max_channels; unsigned int period_list[] = { 4096, /* 23/46ms (stereo/mono 16bit at 44.1kHz) */ }; - int err, channels; /* access type */ err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, @@ -925,15 +1013,18 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - channels = cfg.mode == CFG_MODE_MONO ? 1 : 2; - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - channels, channels); - if (err < 0) - return err; + if (a2dp->sbc_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + min_channels = 1; + else + min_channels = 2; - /* supported rate */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - cfg.rate, cfg.rate); + if (a2dp->sbc_capabilities.channel_mode & (~BT_A2DP_CHANNEL_MODE_MONO)) + max_channels = 2; + else + max_channels = 1; + + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, + min_channels, max_channels); if (err < 0) return err; @@ -949,96 +1040,44 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; - return 0; -} - -static int bluetooth_recvmsg_fd(struct bluetooth_data *data) -{ - char cmsg_b[CMSG_SPACE(sizeof(int))], m; - int err, ret; - struct iovec iov = { &m, sizeof(m) }; - struct msghdr msgh; - struct cmsghdr *cmsg; - - memset(&msgh, 0, sizeof(msgh)); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_control = &cmsg_b; - msgh.msg_controllen = CMSG_LEN(sizeof(int)); - - ret = recvmsg(data->server.fd, &msgh, 0); - if (ret < 0) { - err = errno; - SNDERR("Unable to receive fd: %s (%d)", strerror(err), err); - return -err; + /* supported rates */ + rate_count = 0; + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) { + rate_list[rate_count] = 16000; + rate_count++; } - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) { - data->stream.fd = (*(int *) CMSG_DATA(cmsg)); - DBG("stream_fd=%d", data->stream.fd); - return 0; - } + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) { + rate_list[rate_count] = 32000; + rate_count++; } - return -EINVAL; -} - -static int bluetooth_a2dp_init(struct bluetooth_data *data, - struct ipc_codec_sbc *sbc) -{ - struct bluetooth_a2dp *a2dp = &data->a2dp; - struct ipc_data_cfg *cfg = &data->cfg; - - if (cfg == NULL) { - SNDERR("Error getting codec parameters"); - return -1; + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) { + rate_list[rate_count] = 44100; + rate_count++; } - if (cfg->codec != CFG_CODEC_SBC) - return -1; - - /* FIXME: init using flags? */ - sbc_init(&a2dp->sbc, 0); - a2dp->sbc.rate = cfg->rate; - a2dp->sbc.channels = cfg->mode == CFG_MODE_MONO ? 1 : 2; - if (cfg->mode == CFG_MODE_MONO || cfg->mode == CFG_MODE_JOINT_STEREO) - a2dp->sbc.joint = 1; - a2dp->sbc.allocation = sbc->allocation; - a2dp->sbc.subbands = sbc->subbands; - a2dp->sbc.blocks = sbc->blocks; - a2dp->sbc.bitpool = sbc->bitpool; - a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); + if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) { + rate_list[rate_count] = 48000; + rate_count++; + } - DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", - a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, - a2dp->sbc.bitpool); + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, + rate_count, rate_list); + if (err < 0) + return err; return 0; } -static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, - snd_config_t *conf) +static int bluetooth_parse_config(snd_config_t *conf, + struct bluetooth_alsa_config *bt_config) { - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; snd_config_iterator_t i, next; const char *addr, *pref; const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; - switch (stream) { - case SND_PCM_STREAM_PLAYBACK: - cfg->fd_opt = CFG_FD_OPT_WRITE; - break; - case SND_PCM_STREAM_CAPTURE: - cfg->fd_opt = CFG_FD_OPT_READ; - break; - } + memset(bt_config, 0, sizeof(struct bluetooth_alsa_config)); snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -1056,7 +1095,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - strncpy(pkt->device, addr, 18); + bt_config->has_device = 1; + strncpy(bt_config->device, addr, 18); continue; } @@ -1066,14 +1106,18 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - pkt->role = PKT_ROLE_AUTO; - else if (strcmp(pref, "voice") == 0 || + if (strcmp(pref, "auto") == 0) { + bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY; + bt_config->has_transport = 1; + } else if (strcmp(pref, "voice") == 0 || strcmp(pref, "hfp") == 0) { - pkt->role = PKT_ROLE_VOICE; + bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO; + bt_config->has_transport = 1; } else if (strcmp(pref, "hifi") == 0 || - strcmp(pref, "a2dp") == 0) - pkt->role = PKT_ROLE_HIFI; + strcmp(pref, "a2dp") == 0) { + bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + bt_config->has_transport = 1; + } continue; } @@ -1083,7 +1127,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - cfg->rate = atoi(rate); + bt_config->rate = atoi(rate); + bt_config->has_rate = 1; continue; } @@ -1093,16 +1138,22 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - cfg->mode = CFG_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) - cfg->mode = CFG_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->mode = CFG_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->mode = CFG_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->mode = CFG_MODE_JOINT_STEREO; + if (strcmp(pref, "auto") == 0) { + bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; + bt_config->has_channel_mode = 1; + } else if (strcmp(pref, "mono") == 0) { + bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + bt_config->has_channel_mode = 1; + } else if (strcmp(pref, "dual") == 0) { + bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + bt_config->has_channel_mode = 1; + } else if (strcmp(pref, "stereo") == 0) { + bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + bt_config->has_channel_mode = 1; + } else if (strcmp(pref, "joint") == 0) { + bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + bt_config->has_channel_mode = 1; + } continue; } @@ -1112,12 +1163,16 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - if (strcmp(pref, "auto") == 0) - sbc->allocation = CFG_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) - sbc->allocation = CFG_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - sbc->allocation = CFG_ALLOCATION_SNR; + if (strcmp(pref, "auto") == 0) { + bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO; + bt_config->has_allocation_method = 1; + } else if (strcmp(pref, "loudness") == 0) { + bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + bt_config->has_allocation_method = 1; + } else if (strcmp(pref, "snr") == 0) { + bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR; + bt_config->has_allocation_method = 1; + } continue; } @@ -1127,7 +1182,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->subbands = atoi(subbands); + bt_config->subbands = atoi(subbands); + bt_config->has_subbands = 1; continue; } @@ -1137,7 +1193,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->blocks = atoi(blocks); + bt_config->block_length = atoi(blocks); + bt_config->has_block_length = 1; continue; } @@ -1147,7 +1204,8 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - sbc->bitpool = atoi(bitpool); + bt_config->bitpool = atoi(bitpool); + bt_config->has_bitpool = 1; continue; } @@ -1155,139 +1213,89 @@ static int bluetooth_cfg_init(struct ipc_packet *pkt, snd_pcm_stream_t stream, return -EINVAL; } - pkt->length = sizeof(*cfg) + sizeof(*sbc); - pkt->type = PKT_TYPE_CFG_REQ; - pkt->error = PKT_ERROR_NONE; - return 0; } -static int bluetooth_cfg(struct bluetooth_data *data, snd_pcm_stream_t stream, - snd_config_t *conf) +static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) { - int ret, total; - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - DBG("Sending PKT_TYPE_CFG_REQ..."); - - memset(buf, 0, sizeof(buf)); - - ret = bluetooth_cfg_init(pkt, stream, conf); - if (ret < 0) - return -ret; - - ret = send(data->server.fd, pkt, sizeof(*pkt) + pkt->length, 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - DBG("OK - %d bytes sent. Waiting for response...", ret); - - memset(buf, 0, sizeof(buf)); - - ret = recv(data->server.fd, buf, sizeof(*pkt) + sizeof(*cfg), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - total = ret; - - if (pkt->type != PKT_TYPE_CFG_RSP) { - SNDERR("Unexpected packet type %d received", pkt->type); - return -EINVAL; - } - - if (pkt->error != PKT_ERROR_NONE) { - SNDERR("Error %d while configuring device", pkt->error); - return -pkt->error; - } - - if (cfg->codec != CFG_CODEC_SBC) - goto done; - - ret = recv(data->server.fd, sbc, sizeof(*sbc), 0); - if (ret < 0) - return -errno; - else if (ret == 0) - return -EIO; - - total += ret; - -done: - DBG("OK - %d bytes received", total); + int err; - if (pkt->length != (total - sizeof(struct ipc_packet))) { - SNDERR("Error while configuring device: packet size doesn't match"); - return -EINVAL; + DBG("sending %s", bt_audio_strmsg(msg->msg_type)); + if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) + err = 0; + else { + err = -errno; + SNDERR("Error sending data to audio service: %s(%d)", + strerror(errno), errno); } - memcpy(&data->cfg, cfg, sizeof(*cfg)); - - DBG("Device configuration:"); - - DBG("\n\tfd=%d\n\tfd_opt=%u\n\tpkt_len=%u\n\tsample_size=%u\n\trate=%u", - data->stream.fd, data->cfg.fd_opt, data->cfg.pkt_len, - data->cfg.sample_size, data->cfg.rate); + return err; +} - if (data->cfg.codec == CFG_CODEC_SBC) { - ret = bluetooth_a2dp_init(data, sbc); - if (ret < 0) - return ret; +static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) +{ + int err; + const char *type; + + DBG("trying to receive msg from audio service..."); + if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) { + type = bt_audio_strmsg(inmsg->msg_type); + if (type) { + DBG("Received %s", type); + err = 0; + } else { + err = -EINVAL; + SNDERR("Bogus message type %d " + "received from audio service", + inmsg->msg_type); + } + } else { + err = -errno; + SNDERR("Error receiving data from audio service: %s(%d)", + strerror(errno), errno); } - ret = bluetooth_recvmsg_fd(data); - if (ret < 0) - return ret; + return err; +} - if (data->stream.fd == -1) { - SNDERR("Error while configuring device: could not acquire audio socket"); - return -EINVAL; +static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, + int expected_type) +{ + int err = audioservice_recv(sk, outmsg); + if (err == 0) { + if (outmsg->msg_type != expected_type) { + err = -EINVAL; + SNDERR("Bogus message %s received while " + "%s was expected", + bt_audio_strmsg(outmsg->msg_type), + bt_audio_strmsg(expected_type)); + } } - - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - while (recv(data->stream.fd, data->buffer, data->cfg.pkt_len, - MSG_DONTWAIT) > 0); - - memset(data->buffer, 0, sizeof(data->buffer)); - - return 0; + return err; } static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, snd_config_t *conf) { int sk, err; - struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME - }; - - if (!data) - return -EINVAL; + struct bluetooth_alsa_config *alsa_conf = &data->alsa_config; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *getcaps_req = (void*) buf; + struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf; memset(data, 0, sizeof(struct bluetooth_data)); + err = bluetooth_parse_config(conf, alsa_conf); + if (err < 0) + return err; + data->server.fd = -1; data->stream.fd = -1; - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { - err = errno; - SNDERR("Cannot open socket: %s (%d)", strerror(err), err); - return -err; - } - - DBG("Connecting to address: %s", addr.sun_path + 1); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = errno; - SNDERR("Connection fail", strerror(err), err); - close(sk); - return -err; + sk = bt_audio_service_open(); + if(sk <= 0) { + err = -errno; + goto failed; } data->server.fd = sk; @@ -1296,14 +1304,56 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, data->pipefd[0] = -1; data->pipefd[1] = -1; - if (pipe(data->pipefd) < 0) - return -errno; - if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) - return -errno; - if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) - return -errno; + if (pipe(data->pipefd) < 0) { + err = -errno; + goto failed; + } + if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) { + err = -errno; + goto failed; + } + if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) { + err = -errno; + goto failed; + } + + memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ; + strncpy(getcaps_req->device, alsa_conf->device, 18); + if (alsa_conf->has_transport) + getcaps_req->transport = alsa_conf->transport; + else + getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY; + + getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ? + BT_CAPABILITIES_ACCESS_MODE_WRITE : + BT_CAPABILITIES_ACCESS_MODE_READ); + + err = audioservice_send(data->server.fd, &getcaps_req->h); + if (err < 0) + goto failed; + + err = audioservice_expect(data->server.fd, &getcaps_rsp->h, BT_GETCAPABILITIES_RSP); + if (err < 0) + goto failed; + + if (getcaps_rsp->posix_errno != 0) { + SNDERR("BT_GETCAPABILITIES failed : %s(%d)", + strerror(getcaps_rsp->posix_errno), + getcaps_rsp->posix_errno); + return -getcaps_rsp->posix_errno; + } + + data->transport = getcaps_rsp->transport; + data->link_mtu = getcaps_rsp->link_mtu; + if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP) + data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities; - return bluetooth_cfg(data, stream, conf); + return 0; + +failed: + bt_audio_service_close(sk); + return err; } SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) @@ -1329,7 +1379,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) data->io.mmap_rw = 0; /* No direct mmap communication */ data->io.private_data = data; - if (data->cfg.codec == CFG_CODEC_SBC) + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &bluetooth_a2dp_playback : &bluetooth_a2dp_capture; @@ -1342,7 +1392,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) if (err < 0) goto error; - if (data->cfg.codec == CFG_CODEC_SBC) + if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) err = bluetooth_a2dp_hw_constraint(&data->io); else err = bluetooth_hsp_hw_constraint(&data->io); diff --git a/audio/unix.c b/audio/unix.c index 71eb570a..ea96bd4f 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -79,7 +79,8 @@ struct unix_client { struct headset_data hs; } d; int sock; - int fd_opt; + int access_mode; + int data_fd; /* To be deleted once two phase configuration is fully implemented */ unsigned int req_id; unsigned int cb_id; gboolean (*cancel_stream) (struct device *dev, unsigned int id); @@ -89,6 +90,11 @@ static GSList *clients = NULL; static int unix_sock = -1; +static void unix_ipc_sendmsg(struct unix_client *client, + const bt_audio_msg_header_t *msg); + +static void send_getcapabilities_rsp_error(struct unix_client *client, int err); + static void client_free(struct unix_client *client) { struct a2dp_data *a2dp; @@ -121,7 +127,7 @@ static void client_free(struct unix_client *client) /* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set -to SCM_RIGHTS and the data being an integer value equal to the handle of the +to SCM_RIGHTS and the data being an integer value equal to the handle of the file descriptor to be passed.*/ static int unix_sendmsg_fd(int sock, int fd) { @@ -193,77 +199,29 @@ static void stream_state_changed(struct avdtp_stream *stream, } } -static int unix_send_cfg(int sock, struct ipc_data_cfg *cfg, int fd) -{ - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; - int len, codec_len; - - memset(buf, 0, sizeof(buf)); - - pkt->type = PKT_TYPE_CFG_RSP; - - if (!cfg) { - pkt->error = EINVAL; - len = send(sock, pkt, sizeof(struct ipc_packet), 0); - if (len < 0) - error("send: %s (%d)", strerror(errno), errno); - return len; - } - - debug("fd=%d, fd_opt=%u, pkt_len=%u, sample_size=%u, rate=%u", - fd, cfg->fd_opt, cfg->pkt_len, - cfg->sample_size, cfg->rate); - - if (cfg->codec == CFG_CODEC_SBC) - codec_len = sizeof(struct ipc_codec_sbc); - else - codec_len = 0; - - pkt->error = PKT_ERROR_NONE; - pkt->length = sizeof(struct ipc_data_cfg) + codec_len; - memcpy(pkt->data, cfg, pkt->length); - - len = sizeof(struct ipc_packet) + pkt->length; - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - - debug("%d bytes sent", len); - - if (fd != -1) { - len = unix_sendmsg_fd(sock, fd); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); - debug("%d bytes sent", len); - } - - return 0; -} - static void headset_setup_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; - struct ipc_data_cfg cfg; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; struct headset_data *hs = &client->d.hs; - int fd; client->req_id = 0; if (!dev) { - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); client->dev = NULL; return; } - switch (client->fd_opt) { - case CFG_FD_OPT_READ: + switch (client->access_mode) { + case BT_CAPABILITIES_ACCESS_MODE_READ: hs->lock = HEADSET_LOCK_READ; break; - case CFG_FD_OPT_WRITE: + case BT_CAPABILITIES_ACCESS_MODE_WRITE: hs->lock = HEADSET_LOCK_WRITE; break; - case CFG_FD_OPT_READWRITE: + case BT_CAPABILITIES_ACCESS_MODE_READWRITE: hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; break; default: @@ -273,23 +231,22 @@ static void headset_setup_complete(struct device *dev, void *user_data) if (!headset_lock(dev, hs->lock)) { error("Unable to lock headset"); - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); client->dev = NULL; return; } - memset(&cfg, 0, sizeof(cfg)); + memset(buf, 0, sizeof(buf)); - cfg.fd_opt = client->fd_opt; - cfg.codec = CFG_CODEC_SCO; - cfg.mode = CFG_MODE_MONO; - cfg.pkt_len = 48; - cfg.sample_size = 2; - cfg.rate = 8000; + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; + rsp->access_mode = client->access_mode; + rsp->link_mtu = 48; + rsp->sampling_rate = 8000; - fd = headset_get_sco_fd(dev); + client->data_fd = headset_get_sco_fd(dev); - unix_send_cfg(client->sock, &cfg, fd); + unix_ipc_sendmsg(client, &rsp->h); } static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, @@ -297,17 +254,16 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, void *user_data, struct avdtp_error *err) { struct unix_client *client = user_data; - char buf[sizeof(struct ipc_data_cfg) + sizeof(struct ipc_codec_sbc)]; - struct ipc_data_cfg *cfg = (void *) buf; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; - struct ipc_codec_sbc *sbc = (void *) cfg->data; struct a2dp_data *a2dp = &client->d.a2dp; - int fd; uint16_t imtu, omtu; GSList *caps; + memset(buf, 0, sizeof(buf)); client->req_id = 0; if (!stream) @@ -321,7 +277,7 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, a2dp->sep = sep; a2dp->stream = stream; - if (!avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, &caps)) { + if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, &caps)) { error("Unable to get stream transport"); goto failed; } @@ -340,66 +296,27 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, goto failed; } + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + rsp->access_mode = client->access_mode; /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ - cfg->pkt_len = omtu; - cfg->fd_opt = CFG_FD_OPT_WRITE; + rsp->link_mtu = omtu; sbc_cap = (void *) codec_cap; - cfg->sample_size = 2; - switch (sbc_cap->channel_mode) { - case A2DP_CHANNEL_MODE_MONO: - cfg->mode = CFG_MODE_MONO; - break; - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - cfg->mode = CFG_MODE_DUAL_CHANNEL; - break; - case A2DP_CHANNEL_MODE_STEREO: - cfg->mode = CFG_MODE_STEREO; - break; - case A2DP_CHANNEL_MODE_JOINT_STEREO: - cfg->mode = CFG_MODE_JOINT_STEREO; - break; - } + /* assignations below are ok as soon as newipc.h and a2dp.h are kept */ + /* in sync. However it is not possible to cast a struct to another */ + /* dues to endianess issues */ + rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; + rsp->sbc_capabilities.frequency = sbc_cap->frequency; + rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; + rsp->sbc_capabilities.subbands = sbc_cap->subbands; + rsp->sbc_capabilities.block_length = sbc_cap->block_length; + rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; + rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; - switch (sbc_cap->frequency) { - case A2DP_SAMPLING_FREQ_16000: - cfg->rate = 16000; - break; - case A2DP_SAMPLING_FREQ_32000: - cfg->rate = 32000; - break; - case A2DP_SAMPLING_FREQ_44100: - cfg->rate = 44100; - break; - case A2DP_SAMPLING_FREQ_48000: - cfg->rate = 48000; - break; - } - - cfg->codec = CFG_CODEC_SBC; - sbc->allocation = sbc_cap->allocation_method == A2DP_ALLOCATION_SNR ? - 0x01 : 0x00; - sbc->subbands = sbc_cap->subbands == A2DP_SUBBANDS_4 ? 4 : 8; - - switch (sbc_cap->block_length) { - case A2DP_BLOCK_LENGTH_4: - sbc->blocks = 4; - break; - case A2DP_BLOCK_LENGTH_8: - sbc->blocks = 8; - break; - case A2DP_BLOCK_LENGTH_12: - sbc->blocks = 12; - break; - case A2DP_BLOCK_LENGTH_16: - sbc->blocks = 16; - break; - } - - sbc->bitpool = sbc_cap->max_bitpool; - - unix_send_cfg(client->sock, cfg, fd); + unix_ipc_sendmsg(client, &rsp->h); client->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, client); @@ -412,7 +329,7 @@ failed: a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); avdtp_unref(a2dp->session); @@ -469,7 +386,7 @@ static void create_stream(struct device *dev, struct unix_client *client) return; failed: - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); } static void create_cb(struct device *dev, void *user_data) @@ -477,135 +394,57 @@ static void create_cb(struct device *dev, void *user_data) struct unix_client *client = user_data; if (!dev) - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); else create_stream(dev, client); } -static int cfg_to_caps(struct ipc_data_cfg *cfg, struct sbc_codec_cap *sbc_cap) +static void unix_ipc_sendmsg(struct unix_client *client, + const bt_audio_msg_header_t *msg) { - struct ipc_codec_sbc *sbc = (void *) cfg->data; - - memset(sbc_cap, 0, sizeof(struct sbc_codec_cap)); - - sbc_cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap->cap.media_codec_type = A2DP_CODEC_SBC; - - switch (cfg->rate) { - case 48000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_48000; - break; - case 44100: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; - break; - case 32000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_32000; - break; - case 16000: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_16000; - break; - default: - sbc_cap->frequency = A2DP_SAMPLING_FREQ_44100; - break; - } - - switch (cfg->mode) { - case CFG_MODE_MONO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_MONO; - break; - case CFG_MODE_DUAL_CHANNEL: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - break; - case CFG_MODE_STEREO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - break; - case CFG_MODE_JOINT_STEREO: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - break; - default: - sbc_cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - break; - } - - switch (sbc->allocation) { - case CFG_ALLOCATION_LOUDNESS: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - case CFG_ALLOCATION_SNR: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - default: - sbc_cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - break; - } - - switch (sbc->subbands) { - case 8: - sbc_cap->subbands = A2DP_SUBBANDS_8; - break; - case 4: - sbc_cap->subbands = A2DP_SUBBANDS_4; - break; - default: - sbc_cap->subbands = A2DP_SUBBANDS_8; - break; - } - - switch (sbc->blocks) { - case 16: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; - break; - case 12: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_12; - break; - case 8: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_8; - break; - case 4: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_4; - break; - default: - sbc_cap->block_length = A2DP_BLOCK_LENGTH_16; - break; - } + info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); + if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) + error("Error %s(%d)", strerror(errno), errno); +} - if (sbc->bitpool != 0) { - if (sbc->bitpool > 250) - return -EINVAL; +static void send_getcapabilities_rsp_error(struct unix_client *client, int err) +{ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; - sbc_cap->min_bitpool = sbc->bitpool; - sbc_cap->max_bitpool = sbc->bitpool; - } + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->posix_errno = err; - return 0; + unix_ipc_sendmsg(client, &rsp->h); } -static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int len) +static void handle_getcapabilities_req(struct unix_client *client, + struct bt_getcapabilities_req *req) { struct device *dev; bdaddr_t bdaddr; - struct ipc_data_cfg *cfg = (void *) pkt->data; - struct sbc_codec_cap sbc_cap; - str2ba(pkt->device, &bdaddr); + str2ba(req->device, &bdaddr); + + if (!req->access_mode) { + send_getcapabilities_rsp_error(client, EINVAL); + return; + } - client->fd_opt = cfg->fd_opt; + client->access_mode = req->access_mode; if (client->interface) { g_free(client->interface); client->interface = NULL; } - if (pkt->role == PKT_ROLE_VOICE) + if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - else if (pkt->role == PKT_ROLE_HIFI) + else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) client->interface = g_strdup(AUDIO_SINK_INTERFACE); - if (cfg_to_caps(cfg, &sbc_cap) < 0) - goto failed; - - client->media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, - &sbc_cap, sizeof(sbc_cap)); + client->media_codec = 0; if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) @@ -627,59 +466,83 @@ static void cfg_event(struct unix_client *client, struct ipc_packet *pkt, int le return; failed: - unix_send_cfg(client->sock, NULL, -1); + send_getcapabilities_rsp_error(client, EIO); } -static void ctl_event(struct unix_client *client, - struct ipc_packet *pkt, int len) +static void handle_setconfiguration_req(struct unix_client *client, + struct bt_setconfiguration_req *req) { + /* FIXME: for now we just blindly assume that we receive is the + only valid configuration sent.*/ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_SETCONFIGURATION_RSP; + rsp->posix_errno = 0; + + unix_ipc_sendmsg(client, &rsp->h); } -static int reply_state(int sock, struct ipc_packet *pkt) +static void handle_streamstart_req(struct unix_client *client, + struct bt_streamstart_req *req) { - struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - int len; + /* FIXME : to be really implemented */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_datafd_ind *ind = (void *) buf; - info("status=%u", state->state); + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTART_RSP; + rsp->posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->h); - pkt->type = PKT_TYPE_STATE_RSP; - pkt->length = sizeof(struct ipc_data_state); - pkt->error = PKT_ERROR_NONE; + memset(buf, 0, sizeof(buf)); + ind->h.msg_type = BT_STREAMFD_IND; + unix_ipc_sendmsg(client, &ind->h); - len = sizeof(struct ipc_packet) + sizeof(struct ipc_data_state); - len = send(sock, pkt, len, 0); - if (len < 0) - error("Error %s(%d)", strerror(errno), errno); + if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) + error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); + +} - debug("%d bytes sent", len); +static void handle_streamstop_req(struct unix_client *client, + struct bt_streamstop_req *req) +{ + /* FIXME : to be implemented */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstop_rsp *rsp = (void *) buf; - return 0; + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTOP_RSP; + rsp->posix_errno = 0; + + unix_ipc_sendmsg(client, &rsp->h); } -static void state_event(struct unix_client *client, - struct ipc_packet *pkt, int len) +static void handle_control_req(struct unix_client *client, + struct bt_control_req *req) { -#if 0 - struct ipc_data_state *state = (struct ipc_data_state *) pkt->data; - struct device *dev = client->dev; + /* FIXME: really implement that */ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; - if (len > sizeof(struct ipc_packet)) - device_set_state(dev, state->state); - else - state->state = device_get_state(dev); -#endif + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_CONTROL_RSP; + rsp->posix_errno = 0; - reply_state(client->sock, pkt); + unix_ipc_sendmsg(client, &rsp->h); } static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) { - char buf[IPC_MTU]; - struct ipc_packet *pkt = (void *) buf; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_msg_header_t *msghdr = (void *) buf; struct unix_client *client = data; - int len, len_check; + int len; struct a2dp_data *a2dp = &client->d.a2dp; struct headset_data *hs = &client->d.hs; + const char *type; if (cond & G_IO_NVAL) return FALSE; @@ -709,31 +572,39 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) memset(buf, 0, sizeof(buf)); - len = recv(client->sock, buf, sizeof(buf), 0); + len = recv(client->sock, buf, sizeof(buf), MSG_WAITALL); if (len < 0) { error("recv: %s (%d)", strerror(errno), errno); goto failed; } - len_check = pkt->length + sizeof(struct ipc_packet); - if (len != len_check) { - error("Packet lenght doesn't match"); - goto failed; - } + if ((type = bt_audio_strmsg(msghdr->msg_type))) + info("Audio API: received %s", type); - switch (pkt->type) { - case PKT_TYPE_CFG_REQ: - info("Package PKT_TYPE_CFG_REQ:%u", pkt->role); - cfg_event(client, pkt, len); + switch (msghdr->msg_type) { + case BT_GETCAPABILITIES_REQ: + handle_getcapabilities_req(client, + (struct bt_getcapabilities_req *) msghdr); break; - case PKT_TYPE_STATE_REQ: - info("Package PKT_TYPE_STATE_REQ"); - state_event(client, pkt, len); + case BT_SETCONFIGURATION_REQ: + handle_setconfiguration_req(client, + (struct bt_setconfiguration_req *) msghdr); break; - case PKT_TYPE_CTL_REQ: - info("Package PKT_TYPE_CTL_REQ"); - ctl_event(client, pkt, len); + case BT_STREAMSTART_REQ: + handle_streamstart_req(client, + (struct bt_streamstart_req *) msghdr); break; + case BT_STREAMSTOP_REQ: + handle_streamstop_req(client, + (struct bt_streamstop_req *) msghdr); + break; + case BT_CONTROL_REQ: + handle_control_req(client, + (struct bt_control_req *) msghdr); + break; + default: + error("Audio API: received unexpected packet type %d", + msghdr->msg_type); } return TRUE; @@ -789,7 +660,7 @@ int unix_init(void) { GIOChannel *io; struct sockaddr_un addr = { - AF_UNIX, IPC_SOCKET_NAME + AF_UNIX, BT_IPC_SOCKET_NAME }; int sk, err; -- cgit From d00d4028e7a30da863489e822935ef6d7dc91242 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 22 Nov 2007 15:28:52 +0000 Subject: Get rid of struct timeval completely --- audio/pcm_bluetooth.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index f4a20015..7192f4f3 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -68,12 +68,16 @@ #define SCO_RXBUFS 0x04 #endif -#ifndef TIMESPEC_TO_TIMEVAL -# define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (ts)->tv_sec; \ - (tv)->tv_usec = (ts)->tv_nsec / 1000; \ -} -#endif +/* adapted from glibc sys/time.h timersub() macro */ +#define priv_timespecsub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += 1000000000; \ + } \ + } while (0) struct bluetooth_a2dp { sbc_capabilities_t sbc_capabilities; @@ -149,8 +153,7 @@ static void *playback_hw_thread(void *param) struct bluetooth_data *data = param; unsigned int prev_periods; double period_time; - struct timeval start; - struct timespec start_monotonic; + struct timespec start; struct pollfd fds[2]; int poll_timeout; @@ -167,13 +170,11 @@ static void *playback_hw_thread(void *param) else poll_timeout = MIN_PERIOD_TIME; - clock_gettime(CLOCK_MONOTONIC, &start_monotonic); - TIMESPEC_TO_TIMEVAL(&start, &start_monotonic); + clock_gettime(CLOCK_MONOTONIC, &start); while (1) { unsigned int dtime, periods; - struct timeval cur, delta; - struct timespec cur_monotonic; + struct timespec cur, delta; int ret; if (data->stopped) @@ -182,17 +183,15 @@ static void *playback_hw_thread(void *param) if (data->reset) { DBG("Handle XRUN in hw-thread."); data->reset = 0; - clock_gettime(CLOCK_MONOTONIC, &start_monotonic); - TIMESPEC_TO_TIMEVAL(&start, &start_monotonic); + clock_gettime(CLOCK_MONOTONIC, &start); prev_periods = 0; } - clock_gettime(CLOCK_MONOTONIC, &cur_monotonic); - TIMESPEC_TO_TIMEVAL(&cur, &cur_monotonic); + clock_gettime(CLOCK_MONOTONIC, &cur); - timersub(&cur, &start, &delta); + priv_timespecsub(&cur, &start, &delta); - dtime = delta.tv_sec * 1000000 + delta.tv_usec; + dtime = delta.tv_sec * 1000000 + delta.tv_nsec / 1000; periods = 1.0 * dtime / period_time; if (periods > prev_periods) { @@ -215,7 +214,7 @@ static void *playback_hw_thread(void *param) prev_periods = periods; else { prev_periods = 0; - gettimeofday(&start, 0); + clock_gettime(CLOCK_MONOTONIC, &start); } } -- cgit From db90b606bb8b3ecd2a84a97a4a557ab026287d54 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 22 Nov 2007 15:30:59 +0000 Subject: Support multiple periods --- audio/pcm_bluetooth.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 7192f4f3..815ddd23 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -996,7 +996,9 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) unsigned int rate_count; int err, min_channels, max_channels; unsigned int period_list[] = { - 4096, /* 23/46ms (stereo/mono 16bit at 44.1kHz) */ + 2048, + 4096, /* e.g. 23.2msec/period (stereo 16bit at 44.1kHz) */ + 8192 }; /* access type */ @@ -1027,15 +1029,16 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) if (err < 0) return err; - /* supported block sizes: */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - ARRAY_NELEMS(period_list), period_list); + /* supported buffer sizes + * (can be used as 3*8192, 6*4096, 12*2048, ...) */ + err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, + 8192*3, 8192*3); if (err < 0) return err; - /* period count fixed to 3 as we don't support prefilling */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 3, 3); + /* supported block sizes: */ + err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, + ARRAY_NELEMS(period_list), period_list); if (err < 0) return err; @@ -1062,7 +1065,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) } err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, - rate_count, rate_list); + rate_count, rate_list); if (err < 0) return err; -- cgit From f6f1a6c8a0a3293b799740617f04b245834d2412 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 22 Nov 2007 16:57:33 +0000 Subject: Link librt with the alsa module (needs to be fixed with proper autotools checks later) --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index b04fc302..e29d9fba 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -25,7 +25,7 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* -libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ +libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ -lrt libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c -- cgit From d509d4960ff6fb74bf86bf2791067197d3795440 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 23 Nov 2007 17:00:13 +0000 Subject: Code cleanup. --- audio/gsta2dpsink.c | 21 -------------------- audio/gstsbcenc.c | 57 +++++++++++++++++------------------------------------ audio/gstsbcenc.h | 3 +++ 3 files changed, 21 insertions(+), 60 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 535f6c5e..b24dc8b8 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -831,25 +831,6 @@ static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) return TRUE; } -static GstFlowReturn gst_a2dp_sink_buffer_alloc(GstBaseSink *basesink, - guint64 offset, guint size, GstCaps* caps, - GstBuffer **buf) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - - *buf = gst_buffer_new_and_alloc(size); - if (!(*buf)) { - GST_ERROR_OBJECT(self, "buffer allocation failed"); - return GST_FLOW_ERROR; - } - - gst_buffer_set_caps(*buf, caps); - - GST_BUFFER_OFFSET(*buf) = offset; - - return GST_FLOW_OK; -} - static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); @@ -870,8 +851,6 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps); basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); - basesink_class->buffer_alloc = - GST_DEBUG_FUNCPTR(gst_a2dp_sink_buffer_alloc); g_object_class_install_property(object_class, PROP_DEVICE, g_param_spec_string("device", "Device", diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 021ecacf..185151f5 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -345,23 +345,11 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) enc->sbc.bitpool = bitpool; enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode); enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); - - return TRUE; -} - -static gboolean gst_sbc_enc_change_caps(GstSbcEnc *enc, GstCaps *caps) -{ - GST_INFO_OBJECT(enc, "Changing srcpad caps (renegotiation)"); - - if (!gst_pad_accept_caps(enc->srcpad, caps)) { - GST_WARNING_OBJECT(enc, "Src pad refused caps"); - return FALSE; - } - - if (!gst_sbc_enc_fill_sbc_params(enc, caps)) { - GST_ERROR_OBJECT(enc, "couldn't get sbc parameters from caps"); - return FALSE; - } + enc->codesize = sbc_get_codesize(&enc->sbc); + enc->frame_length = sbc_get_frame_length(&enc->sbc); + enc->frame_duration = sbc_get_frame_duration(&enc->sbc); + GST_DEBUG("codesize: %d, frame_length: %d, frame_duration: %d", + enc->codesize, enc->frame_length, enc->frame_duration); return TRUE; } @@ -371,45 +359,36 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); GstAdapter *adapter = enc->adapter; GstFlowReturn res = GST_FLOW_OK; - gint codesize, frame_len; - codesize = sbc_get_codesize(&enc->sbc); - frame_len = sbc_get_frame_length(&enc->sbc); gst_adapter_push(adapter, buffer); - while (gst_adapter_available(adapter) >= codesize && res == GST_FLOW_OK) { + while (gst_adapter_available(adapter) >= enc->codesize && + res == GST_FLOW_OK) { GstBuffer *output; GstCaps *caps; const guint8 *data; int consumed; caps = GST_PAD_CAPS(enc->srcpad); - res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, GST_BUFFER_OFFSET_NONE, - frame_len, caps, &output); + enc->frame_length, caps, + &output); + if (res != GST_FLOW_OK) + goto done; - data = gst_adapter_peek(adapter, codesize); - consumed = sbc_encode(&enc->sbc, (gpointer) data, codesize, - GST_BUFFER_DATA(output), frame_len, - NULL); + data = gst_adapter_peek(adapter, enc->codesize); + consumed = sbc_encode(&enc->sbc, (gpointer) data, + enc->codesize, + GST_BUFFER_DATA(output), + GST_BUFFER_SIZE(output), NULL); if (consumed <= 0) { - GST_ERROR ("comsumed < 0, codesize: %d", codesize); + GST_ERROR ("comsumed < 0, codesize: %d", + enc->codesize); break; } gst_adapter_flush(adapter, consumed); - if (res != GST_FLOW_OK) - goto done; - - if (!gst_caps_is_equal(caps, GST_BUFFER_CAPS(output))) - if (!gst_sbc_enc_change_caps(enc, - GST_BUFFER_CAPS(output))) { - res = GST_FLOW_ERROR; - GST_ERROR_OBJECT(enc, "couldn't renegotiate caps"); - goto done; - } - GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); res = gst_pad_push(enc->srcpad, output); diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index f65bcc94..d81428c9 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -55,6 +55,9 @@ struct _GstSbcEnc { gint blocks; gint allocation; gint subbands; + gint codesize; + gint frame_length; + gint frame_duration; sbc_t sbc; }; -- cgit From 6dda5e4081749ace9d58aae0b416f9ac86ef9f70 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Nov 2007 10:24:56 +0000 Subject: Add autoconf magic for the librt linking --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index e29d9fba..b04fc302 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -25,7 +25,7 @@ alsa_LTLIBRARIES = libasound_module_pcm_bluetooth.la libasound_module_ctl_blueto libasound_module_pcm_bluetooth_la_SOURCES = pcm_bluetooth.c rtp.h ipc.h ipc.c libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex [_]*snd_pcm_.* -libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ -lrt +libasound_module_pcm_bluetooth_la_LIBADD = @SBC_LIBS@ @ALSA_LIBS@ libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@ @SBC_CFLAGS@ libasound_module_ctl_bluetooth_la_SOURCES = ctl_bluetooth.c rtp.h ipc.h ipc.c -- cgit From 3ff24ea225437af91db69a069caa65b00ed642d9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Nov 2007 12:30:23 +0000 Subject: Mention Kai's contributions --- audio/pcm_bluetooth.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 815ddd23..a5bfcdf9 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -2,6 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * + * Copyright (C) 2006-2007 Nokia Corporation * Copyright (C) 2004-2007 Marcel Holtmann * * -- cgit From 7e88afe4f8307c092172ff3c3b76c2f95ab00293 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 26 Nov 2007 13:43:17 +0000 Subject: Update services to new error codes and helper functions --- audio/Makefile.am | 2 +- audio/error.c | 109 ------------------------------------------------------ audio/error.h | 35 ------------------ audio/headset.c | 52 +++++++++++++------------- audio/manager.c | 55 ++++++++++++++------------- audio/sink.c | 19 +++++----- 6 files changed, 65 insertions(+), 207 deletions(-) delete mode 100644 audio/error.c delete mode 100644 audio/error.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index b04fc302..f9284b05 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -12,7 +12,7 @@ service_PROGRAMS = bluetoothd-service-audio bluetoothd_service_audio_SOURCES = main.c \ manager.h manager.c headset.h headset.c ipc.h ipc.c unix.h unix.c \ - error.h error.c device.h device.c gateway.h gateway.c \ + device.h device.c gateway.h gateway.c \ sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ diff --git a/audio/error.c b/audio/error.c deleted file mode 100644 index 4e9a880b..00000000 --- a/audio/error.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 "dbus.h" - -#include "error.h" -#include "logging.h" - -#define AUDIO_ERROR_INTERFACE "org.bluez.audio.Error" - -/* FIXME: Remove these once global error functions exist */ -static DBusHandlerResult error_reply(DBusConnection *conn, DBusMessage *msg, - const char *name, const char *descr) -{ - DBusMessage *derr; - - if (!conn || !msg) - return DBUS_HANDLER_RESULT_HANDLED; - - derr = dbus_message_new_error(msg, name, descr); - if (!derr) { - error("Unable to allocate new error return"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - return send_message_and_unref(conn, derr); -} - -DBusHandlerResult err_invalid_args(DBusConnection *conn, - DBusMessage *msg, const char *descr) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".InvalidArguments", - descr ? descr : "Invalid arguments in method call"); -} - -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".AlreadyConnected", - "Already connected to a device"); -} - -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".NotConnected", - "Not connected to any device"); -} - -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".NotSupported", - "The service is not supported by the remote device"); -} - -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".ConnectFailed", err); -} - -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, - AUDIO_ERROR_INTERFACE ".DoesNotExist", - "Does not exist"); -} - -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".NotAvailable", - "Not available"); -} - -DBusHandlerResult err_failed(DBusConnection *conn, - DBusMessage *msg, const char *dsc) -{ - return error_reply(conn, msg, AUDIO_ERROR_INTERFACE ".Failed", dsc); -} diff --git a/audio/error.h b/audio/error.h deleted file mode 100644 index 377dea5b..00000000 --- a/audio/error.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * 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 - * 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 - * - */ - -DBusHandlerResult err_invalid_args(DBusConnection *conn, - DBusMessage *msg, const char *descr); -DBusHandlerResult err_already_connected(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_connected(DBusConnection *conn, DBusMessage * msg); -DBusHandlerResult err_not_supported(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_connect_failed(DBusConnection *conn, - DBusMessage *msg, const char *err); -DBusHandlerResult err_does_not_exist(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_not_available(DBusConnection *conn, DBusMessage *msg); -DBusHandlerResult err_failed(DBusConnection *conn, - DBusMessage *msg, const char *dsc); diff --git a/audio/headset.c b/audio/headset.c index 175ad861..6cb5406d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -384,7 +384,7 @@ static gboolean finalize_stream_setup(struct device *dev) static void pending_connect_failed(struct pending_connect *c, struct device *dev) { if (c->msg) - err_connect_failed(dev->conn, c->msg, strerror(c->err)); + error_connection_attempt_failed(dev->conn, c->msg, c->err); if (c->cb) c->cb(NULL, c->cb_data); pending_connect_free(c); @@ -680,7 +680,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) failed_not_supported: if (c->msg) { - err_not_supported(device->conn, c->msg); + error_not_supported(device->conn, c->msg); dbus_message_unref(c->msg); c->msg = NULL; } @@ -721,10 +721,10 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (c->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(device->conn, c->msg, - strerror(EHOSTDOWN)); + error_connection_attempt_failed(device->conn, c->msg, + EHOSTDOWN); else - err_not_supported(device->conn, c->msg); + error_not_supported(device->conn, c->msg); } dbus_error_free(&derr); goto failed; @@ -738,21 +738,21 @@ static void get_handles_reply(DBusPendingCall *call, void *data) error("Unable to get args from reply: %s", derr.message); dbus_error_free(&derr); if (c->msg) - err_not_supported(device->conn, c->msg); + error_not_supported(device->conn, c->msg); goto failed; } if (!array) { error("get_handles_reply: Unable to get handle array from reply"); if (c->msg) - err_not_supported(device->conn, c->msg); + error_not_supported(device->conn, c->msg); goto failed; } if (array_len < 1) { debug("No record handles found"); if (c->msg) - err_not_supported(device->conn, c->msg); + error_not_supported(device->conn, c->msg); goto failed; } @@ -765,7 +765,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (!msg) { error("Unable to allocate new method call"); if (c->msg) - err_connect_failed(device->conn, c->msg, strerror(ENOMEM)); + error_out_of_memory(device->conn, c->msg); goto failed; } @@ -780,7 +780,7 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); if (c->msg) - err_connect_failed(device->conn, c->msg, strerror(EIO)); + error_connection_attempt_failed(device->conn, c->msg, EIO); goto failed; } @@ -945,7 +945,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); headset_set_state(device, HEADSET_STATE_CONNECTED); send_message_and_unref(conn, reply); @@ -988,7 +988,7 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NEED_MEMORY; if (hs->state == HEADSET_STATE_DISCONNECTED) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); headset_set_state(device, HEADSET_STATE_DISCONNECTED); ba2str(&device->dst, hs_address); @@ -1030,7 +1030,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, int err; if (hs->state > HEADSET_STATE_DISCONNECTED) - return err_already_connected(conn, msg); + return error_already_connected(conn, msg); c = g_try_new0(struct pending_connect, 1); if (!c) { @@ -1048,7 +1048,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, error: pending_connect_free(c); - return err_connect_failed(conn, msg, strerror(-err)); + return error_connection_attempt_failed(conn, msg, -err); } static gboolean ring_timer_cb(gpointer data) @@ -1069,7 +1069,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1082,7 +1082,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(conn, msg, "Failed"); + return error_failed(conn, msg, "Failed"); } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); @@ -1102,7 +1102,7 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1131,10 +1131,10 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, int err; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS) - return err_already_connected(conn, msg); + return error_already_connected(conn, msg); c = g_try_new0(struct pending_connect, 1); if (!c) @@ -1145,7 +1145,7 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, err = sco_connect(device, c); if (err < 0) { pending_connect_free(c); - return err_failed(conn, msg, strerror(-err)); + return error_failed(conn, msg, strerror(-err)); } return DBUS_HANDLER_RESULT_HANDLED; @@ -1161,7 +1161,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0) - return err_not_available(conn, msg); + return error_not_available(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1187,7 +1187,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0) - return err_not_available(conn, msg); + return error_not_available(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -1215,20 +1215,20 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, char str[13]; if (hs->state < HEADSET_STATE_CONNECTED) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); dbus_error_init(&derr); dbus_message_get_args(msg, &derr, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { - err_invalid_args(conn, msg, derr.message); + error_invalid_arguments(conn, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } if (gain > 15) - return err_invalid_args(conn, msg, + return error_invalid_arguments(conn, msg, "Must be less than or equal to 15"); reply = dbus_message_new_method_return(msg); @@ -1242,7 +1242,7 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, if (headset_send(device->headset, str) != G_IO_ERROR_NONE) { dbus_message_unref(reply); - return err_failed(conn, msg, "Unable to send to headset"); + return error_failed(conn, msg, "Unable to send to headset"); } done: diff --git a/audio/manager.c b/audio/manager.c index d80fb962..507f60ac 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -327,7 +327,7 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (dbus_error_is_set(&derr)) { error("Unable to get message args"); success = FALSE; - err_failed(connection, data->msg, derr.message); + error_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto done; } @@ -336,14 +336,14 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) if (!data->records) { debug("No audio audio related service records were found"); success = FALSE; - err_not_supported(connection, data->msg); + error_not_supported(connection, data->msg); goto done; } reply = dbus_message_new_method_return(data->msg); if (!reply) { success = FALSE; - err_failed(connection, data->msg, "Out of memory"); + error_failed(connection, data->msg, "Out of memory"); goto done; } @@ -392,13 +392,14 @@ static void get_record_reply(DBusPendingCall *call, dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { + /* FIXME : forward error message as is */ error("GetRemoteServiceRecord failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(connection, data->msg, - strerror(EHOSTDOWN)); + error_connection_attempt_failed(connection, data->msg, + EHOSTDOWN); else - err_failed(connection, data->msg, derr.message); + error_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto failed; } @@ -406,7 +407,7 @@ static void get_record_reply(DBusPendingCall *call, if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, DBUS_TYPE_INVALID)) { - err_failed(connection, data->msg, + error_failed(connection, data->msg, "Unable to get args from GetRecordReply"); goto failed; } @@ -452,7 +453,7 @@ static void get_next_record(struct audio_sdp_data *data) "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - err_connect_failed(connection, data->msg, strerror(ENOMEM)); + error_out_of_memory(connection, data->msg); finish_sdp(data, FALSE); return; } @@ -471,7 +472,7 @@ static void get_next_record(struct audio_sdp_data *data) if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); - err_connect_failed(connection, data->msg, strerror(EIO)); + error_connection_attempt_failed(connection, data->msg, EIO); finish_sdp(data, FALSE); return; } @@ -506,12 +507,14 @@ static void get_handles_reply(DBusPendingCall *call, dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { + /* FIXME : forward error message as is */ error("GetRemoteServiceHandles failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - err_connect_failed(connection, data->msg, strerror(EHOSTDOWN)); + error_connection_attempt_failed(connection, data->msg, + EHOSTDOWN); else - err_failed(connection, data->msg, derr.message); + error_failed(connection, data->msg, derr.message); dbus_error_free(&derr); goto failed; } @@ -520,7 +523,7 @@ static void get_handles_reply(DBusPendingCall *call, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array, &array_len, DBUS_TYPE_INVALID)) { - err_failed(connection, data->msg, + error_failed(connection, data->msg, "Unable to get args from reply"); goto failed; } @@ -571,7 +574,7 @@ static DBusHandlerResult get_handles(const char *uuid, "org.bluez.Adapter", "GetRemoteServiceHandles"); if (!msg) { - err_failed(connection, data->msg, + error_failed(connection, data->msg, "Could not create a new dbus message"); goto failed; } @@ -583,7 +586,7 @@ static DBusHandlerResult get_handles(const char *uuid, DBUS_TYPE_INVALID); if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { - err_failed(connection, data->msg, + error_failed(connection, data->msg, "Sending GetRemoteServiceHandles failed"); goto failed; } @@ -736,7 +739,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } @@ -826,18 +829,18 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, if (!dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return err_does_not_exist(connection, msg); + return error_device_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -907,7 +910,7 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID); if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } @@ -916,7 +919,7 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, device = manager_find_device(&bda, NULL, FALSE); if (!device) - return err_does_not_exist(conn, msg); + return error_device_does_not_exist(conn, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -935,12 +938,12 @@ static DBusHandlerResult am_default_device(DBusConnection *conn, DBusMessage *reply; if (!default_dev) - return err_does_not_exist(connection, msg); + return error_device_does_not_exist(connection, msg); if (default_dev->headset == NULL && dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, "DefaultHeadset")) - return err_does_not_exist(connection, msg); + return error_device_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -966,18 +969,18 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, if (!dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); return DBUS_HANDLER_RESULT_HANDLED; } if (dbus_error_is_set(&derr)) { - err_invalid_args(connection, msg, derr.message); + error_invalid_arguments(connection, msg, derr.message); dbus_error_free(&derr); return DBUS_HANDLER_RESULT_HANDLED; } match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return err_does_not_exist(connection, msg); + return error_device_does_not_exist(connection, msg); reply = dbus_message_new_method_return(msg); if (!reply) @@ -999,7 +1002,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); else - return err_does_not_exist(connection, msg); + return error_device_does_not_exist(connection, msg); default_dev = device; device_store(device, TRUE); diff --git a/audio/sink.c b/audio/sink.c index e4fedb12..b1fc5b2b 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -148,7 +148,7 @@ static gboolean stream_setup_retry(gpointer user_data) send_message_and_unref(pending->conn, reply); } else { debug("Stream setup failed, after XCASE connect:connect"); - err_failed(pending->conn, pending->msg, "Stream setup failed"); + error_failed(pending->conn, pending->msg, "Stream setup failed"); } sink->connect = NULL; @@ -183,7 +183,7 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, stream_setup_retry, sink); } else { sink->connect = NULL; - err_failed(pending->conn, pending->msg, "Stream setup failed"); + error_failed(pending->conn, pending->msg, "Stream setup failed"); pending_request_free(pending); debug("Stream setup failed : %s", avdtp_strerror(err)); } @@ -202,14 +202,14 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->session = avdtp_get(&dev->src, &dev->dst); if (!sink->session) - return err_connect_failed(conn, msg, + return error_failed(conn, msg, "Unable to get a session"); if (sink->connect || sink->disconnect) - return err_connect_failed(conn, msg, "Connect in progress"); + return error_in_progress(conn, msg, "Device connection already in progress"); if (sink->state >= AVDTP_STATE_OPEN) - return err_already_connected(conn, msg); + return error_already_connected(conn, msg); pending = g_new0(struct pending_request, 1); pending->conn = dbus_connection_ref(conn); @@ -224,8 +224,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->connect = NULL; avdtp_unref(sink->session); sink->session = NULL; - return err_connect_failed(conn, msg, - "Failed to request a stream"); + return error_failed(conn, msg, "Failed to request a stream"); } debug("stream creation in progress"); @@ -244,10 +243,10 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, int err; if (!sink->session) - return err_not_connected(conn, msg); + return error_not_connected(conn, msg); if (sink->connect || sink->disconnect) - return err_failed(conn, msg, strerror(EBUSY)); + return error_failed(conn, msg, strerror(EBUSY)); if (sink->state < AVDTP_STATE_OPEN) { DBusMessage *reply = dbus_message_new_method_return(msg); @@ -260,7 +259,7 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, err = avdtp_close(sink->session, sink->stream); if (err < 0) - return err_failed(conn, msg, strerror(-err)); + return error_failed(conn, msg, strerror(-err)); pending = g_new0(struct pending_request, 1); pending->conn = dbus_connection_ref(conn); -- 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') 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 9a6980f36017217d56c34d736dd31128e2e73cb4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 27 Nov 2007 09:15:54 +0000 Subject: Enable AVRCP by default --- audio/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 61ca1e3e..52396beb 100644 --- a/audio/main.c +++ b/audio/main.c @@ -53,7 +53,7 @@ static struct enabled_interfaces enabled = { .gateway = FALSE, .sink = TRUE, .source = FALSE, - .control = FALSE, + .control = TRUE, .target = FALSE, }; -- cgit From 2934e194f3ffe754e18477113c870a7b98f88454 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 3 Dec 2007 22:41:29 +0000 Subject: Handle new ipc messages properly and adapt the plugins. --- audio/a2dp.c | 712 ++++++++++++++++++++++---------------------------- audio/a2dp.h | 26 +- audio/avdtp.c | 37 ++- audio/avdtp.h | 2 + audio/gsta2dpsink.c | 73 ++++-- audio/ipc.h | 8 +- audio/pcm_bluetooth.c | 154 ++++++++++- audio/sink.c | 212 +++++++++++++-- audio/unix.c | 581 +++++++++++++++++++++++++++++++--------- 9 files changed, 1225 insertions(+), 580 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 83441617..bf644359 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -43,12 +43,10 @@ #include "sink.h" #include "a2dp.h" -#define MAX_BITPOOL 64 -#define MIN_BITPOOL 2 - /* The duration that streams without users are allowed to stay in * STREAMING state. */ #define SUSPEND_TIMEOUT 5000 +#define RECONFIGURE_TIMEOUT 500 #ifndef MIN # define MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -69,20 +67,23 @@ struct a2dp_sep { gboolean starting; }; -struct a2dp_stream_cb { - a2dp_stream_cb_t cb; +struct a2dp_setup_cb { + a2dp_config_cb_t config_cb; + a2dp_stream_cb_t resume_cb; + a2dp_stream_cb_t suspend_cb; void *user_data; int id; }; -struct a2dp_stream_setup { +struct a2dp_setup { struct avdtp *session; struct a2dp_sep *sep; struct avdtp_stream *stream; - struct avdtp_service_capability *media_codec; - gboolean start; + GSList *client_caps; + gboolean reconfigure; gboolean canceled; GSList *cb; + int ref; }; static DBusConnection *connection = NULL; @@ -94,9 +95,20 @@ static uint32_t source_record_id = 0; static uint32_t sink_record_id = 0; static GSList *setups = NULL; +static unsigned int cb_id = 0; + +static struct a2dp_setup *setup_ref(struct a2dp_setup *setup) +{ + setup->ref++; + + debug("setup_ref(%p): ref=%d", setup, setup->ref); + + return setup; +} -static void stream_setup_free(struct a2dp_stream_setup *s) +static void setup_free(struct a2dp_setup *s) { + debug("setup_free(%p)", s); setups = g_slist_remove(setups, s); if (s->session) avdtp_unref(s->session); @@ -105,6 +117,16 @@ static void stream_setup_free(struct a2dp_stream_setup *s) g_free(s); } +static void setup_unref(struct a2dp_setup *setup) +{ + setup->ref--; + + debug("setup_unref(%p): ref=%d", setup, setup->ref); + + if (setup->ref <= 0) + setup_free(setup); +} + static struct device *a2dp_get_dev(struct avdtp *session) { bdaddr_t addr; @@ -114,35 +136,97 @@ static struct device *a2dp_get_dev(struct avdtp *session) return manager_device_connected(&addr, A2DP_SOURCE_UUID); } -static gboolean finalize_stream_setup(struct a2dp_stream_setup *s, struct avdtp_error *err) +static gboolean finalize_config(struct a2dp_setup *s, struct avdtp_error *err) { GSList *l; + setup_ref(s); for (l = s->cb; l != NULL; l = l->next) { - struct a2dp_stream_cb *cb = l->data; - - cb->cb(s->session, s->sep, s->stream, cb->user_data, err); + struct a2dp_setup_cb *cb = l->data; + + if (cb->config_cb) { + cb->config_cb(s->session, s->sep, s->stream, err, + cb->user_data); + cb->config_cb = NULL; + setup_unref(s); + } } - stream_setup_free(s); + setup_unref(s); return FALSE; } -static gboolean finalize_stream_setup_errno(struct a2dp_stream_setup *s, int err) +static gboolean finalize_config_errno(struct a2dp_setup *s, int err) { struct avdtp_error avdtp_err; avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); - return finalize_stream_setup(s, err ? &avdtp_err : NULL); + return finalize_config(s, err ? &avdtp_err : NULL); } -static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session) +static gboolean finalize_resume(struct a2dp_setup *s, struct avdtp_error *err) +{ + GSList *l; + + setup_ref(s); + for (l = s->cb; l != NULL; l = l->next) { + struct a2dp_setup_cb *cb = l->data; + + if (cb->resume_cb) { + cb->resume_cb(s->session, err, cb->user_data); + cb->resume_cb = NULL; + setup_unref(s); + } + } + + setup_unref(s); + return FALSE; +} + +static gboolean finalize_resume_errno(struct a2dp_setup *s, int err) +{ + struct avdtp_error avdtp_err; + + avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + + return finalize_resume(s, err ? &avdtp_err : NULL); +} + +static gboolean finalize_suspend(struct a2dp_setup *s, struct avdtp_error *err) +{ + GSList *l; + + setup_ref(s); + for (l = s->cb; l != NULL; l = l->next) { + struct a2dp_setup_cb *cb = l->data; + + if (cb->suspend_cb) { + cb->suspend_cb(s->session, err, cb->user_data); + cb->suspend_cb = NULL; + setup_unref(s); + } + } + + setup_unref(s); + return FALSE; +} + +static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err) +{ + struct avdtp_error avdtp_err; + + avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + + return finalize_suspend(s, err ? &avdtp_err : NULL); +} + +static struct a2dp_setup *find_setup_by_session(struct avdtp *session) { GSList *l; for (l = setups; l != NULL; l = l->next) { - struct a2dp_stream_setup *setup = l->data; + struct a2dp_setup *setup = l->data; if (setup->session == session) return setup; @@ -151,12 +235,12 @@ static struct a2dp_stream_setup *find_setup_by_session(struct avdtp *session) return NULL; } -static struct a2dp_stream_setup *find_setup_by_dev(struct device *dev) +static struct a2dp_setup *find_setup_by_dev(struct device *dev) { GSList *l; for (l = setups; l != NULL; l = l->next) { - struct a2dp_stream_setup *setup = l->data; + struct a2dp_setup *setup = l->data; struct device *setup_dev = a2dp_get_dev(setup->session); if (setup_dev == dev) @@ -186,197 +270,9 @@ static void stream_state_changed(struct avdtp_stream *stream, avdtp_unref(sep->session); sep->session = NULL; } - + sep->stream = NULL; -} - -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) -{ - switch (freq) { - case A2DP_SAMPLING_FREQ_16000: - case A2DP_SAMPLING_FREQ_32000: - return 53; - case A2DP_SAMPLING_FREQ_44100: - switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 31; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: - return 53; - default: - error("Invalid channel mode %u", mode); - return 53; - } - case A2DP_SAMPLING_FREQ_48000: - switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 29; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: - return 51; - default: - error("Invalid channel mode %u", mode); - return 51; - } - default: - error("Invalid sampling freq %u", freq); - return 53; - } -} - -static gboolean select_sbc_params(struct sbc_codec_cap *cap, - struct sbc_codec_cap *supported) -{ - uint max_bitpool, min_bitpool; - - memset(cap, 0, sizeof(struct sbc_codec_cap)); - - cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - cap->cap.media_codec_type = A2DP_CODEC_SBC; - - if (supported->frequency & A2DP_SAMPLING_FREQ_44100) - cap->frequency = A2DP_SAMPLING_FREQ_44100; - else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) - cap->frequency = A2DP_SAMPLING_FREQ_48000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) - cap->frequency = A2DP_SAMPLING_FREQ_32000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) - cap->frequency = A2DP_SAMPLING_FREQ_16000; - else { - error("No supported frequencies"); - return FALSE; - } - - if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) - cap->channel_mode = A2DP_CHANNEL_MODE_MONO; - else { - error("No supported channel modes"); - return FALSE; - } - - if (supported->block_length & A2DP_BLOCK_LENGTH_16) - cap->block_length = A2DP_BLOCK_LENGTH_16; - else if (supported->block_length & A2DP_BLOCK_LENGTH_12) - cap->block_length = A2DP_BLOCK_LENGTH_12; - else if (supported->block_length & A2DP_BLOCK_LENGTH_8) - cap->block_length = A2DP_BLOCK_LENGTH_8; - else if (supported->block_length & A2DP_BLOCK_LENGTH_4) - cap->block_length = A2DP_BLOCK_LENGTH_4; - else { - error("No supported block lengths"); - return FALSE; - } - - if (supported->subbands & A2DP_SUBBANDS_8) - cap->subbands = A2DP_SUBBANDS_8; - else if (supported->subbands & A2DP_SUBBANDS_4) - cap->subbands = A2DP_SUBBANDS_4; - else { - error("No supported subbands"); - return FALSE; - } - - if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - else if (supported->allocation_method & A2DP_ALLOCATION_SNR) - cap->allocation_method = A2DP_ALLOCATION_SNR; - - min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); - max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), - supported->max_bitpool); - - cap->min_bitpool = min_bitpool; - cap->max_bitpool = max_bitpool; - - return TRUE; -} -static gboolean a2dp_select_capabilities(struct avdtp *session, - struct avdtp_remote_sep *rsep, - GSList **caps) -{ - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; - struct a2dp_stream_setup *setup; - - setup = find_setup_by_session(session); - if (!setup) - return FALSE; - - if (setup->media_codec) { - memcpy(&sbc_cap, setup->media_codec->data, sizeof(sbc_cap)); - } else { - media_codec = avdtp_get_codec(rsep); - if (!media_codec) - return FALSE; - - select_sbc_params(&sbc_cap, - (struct sbc_codec_cap *) media_codec->data); - } - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); - - *caps = g_slist_append(*caps, media_codec); - - - return TRUE; -} - -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, - void *user_data) -{ - struct avdtp_local_sep *lsep; - struct avdtp_remote_sep *rsep; - struct a2dp_stream_setup *setup; - GSList *caps = NULL; - int posix_err; - - setup = find_setup_by_session(session); - - if (!setup) - return; - - if (err || setup->canceled) { - setup->stream = NULL; - finalize_stream_setup(setup, err); - return; - } - - debug("Discovery complete"); - - if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, - A2DP_CODEC_SBC, &lsep, &rsep) < 0) { - error("No matching ACP and INT SEPs found"); - finalize_stream_setup_errno(setup, -EINVAL); - return; - } - - if (!a2dp_select_capabilities(session, rsep, &caps)) { - error("Unable to select remote SEP capabilities"); - finalize_stream_setup_errno(setup, -EINVAL); - return; - } - - posix_err = avdtp_set_configuration(session, rsep, lsep, caps, - &setup->stream); - if (posix_err < 0) { - error("avdtp_set_configuration: %s", strerror(-posix_err)); - finalize_stream_setup_errno(setup, posix_err); - } } static gboolean setconf_ind(struct avdtp *session, @@ -490,7 +386,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; + struct a2dp_setup *setup; struct device *dev; int ret; @@ -503,7 +399,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { if (setup) - finalize_stream_setup(setup, err); + finalize_config(setup, err); return; } @@ -523,7 +419,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, error("Error on avdtp_open %s (%d)", strerror(-ret), -ret); setup->stream = NULL; - finalize_stream_setup_errno(setup, ret); + finalize_config_errno(setup, ret); } } @@ -569,8 +465,7 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; - int posix_err; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Open_Cfm"); @@ -584,28 +479,19 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (setup->canceled) { if (!err) avdtp_close(session, stream); - stream_setup_free(setup); + setup_unref(setup); return; } + if (setup->reconfigure) + setup->reconfigure = FALSE; + if (err) { setup->stream = NULL; - finalize_stream_setup(setup, err); - return; - } - - if (setup->start) { - posix_err = avdtp_start(session, stream); - if (posix_err == 0) - return; - - error("avdtp_start failed"); - setup->stream = NULL; + finalize_config(setup, err); } else - posix_err = 0; - - finalize_stream_setup_errno(setup, -posix_err); + finalize_config(setup, 0); } static gboolean suspend_timeout(struct a2dp_sep *sep) @@ -647,7 +533,7 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Start_Cfm"); @@ -661,16 +547,16 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (setup->canceled) { if (!err) avdtp_close(session, stream); - stream_setup_free(setup); + setup_unref(setup); return; } if (err) { setup->stream = NULL; - finalize_stream_setup(setup, err); + finalize_resume(setup, err); } else - finalize_stream_setup_errno(setup, 0); + finalize_resume_errno(setup, 0); } static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -699,8 +585,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; - int posix_err; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); @@ -714,15 +599,11 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (err) { - finalize_stream_setup(setup, err); - return; - } - - if (setup->start) { - posix_err = avdtp_start(session, stream); - if (posix_err < 0) - finalize_stream_setup_errno(setup, posix_err); + setup->stream = NULL; + finalize_suspend(setup, err); } + else + finalize_suspend_errno(setup, 0); } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -739,13 +620,39 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } +static gboolean reconfigure(gpointer data) +{ + struct a2dp_setup *setup = data; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + int posix_err; + + posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK, + AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + &lsep, &rsep); + if (posix_err < 0) { + error("No matching ACP and INT SEPs found"); + finalize_config_errno(setup, posix_err); + } + + posix_err = avdtp_set_configuration(setup->session, rsep, lsep, + setup->client_caps, + &setup->stream); + if (posix_err < 0) { + error("avdtp_set_configuration: %s", + strerror(-posix_err)); + finalize_config_errno(setup, posix_err); + } + + return FALSE; +} + static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; - int posix_err; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); @@ -757,28 +664,18 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, return; if (setup->canceled) { - stream_setup_free(setup); + setup_unref(setup); return; } if (err) { setup->stream = NULL; - finalize_stream_setup(setup, err); + finalize_config(setup, err); return; } - if (setup->start) { - posix_err = avdtp_discover(session, discovery_complete, setup); - if (posix_err == 0) - return; - - error("avdtp_discover failed"); - setup->stream = NULL; - } - else - posix_err = 0; - - finalize_stream_setup_errno(setup, -posix_err); + if (setup->reconfigure) + g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -802,11 +699,18 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Cfm"); else debug("SBC Source: Abort_Cfm"); + + setup = find_setup_by_session(session); + if (!setup) + return; + + setup_unref(setup); } static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -826,8 +730,7 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_stream_setup *setup; - int posix_err; + struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); @@ -841,28 +744,16 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (setup->canceled) { if (!err) avdtp_close(session, stream); - stream_setup_free(setup); + setup_unref(setup); return; } if (err) { setup->stream = NULL; - finalize_stream_setup(setup, err); - return; - } - - if (setup->start) { - posix_err = avdtp_start(session, stream); - if (posix_err == 0) - return; - - error("avdtp_start failed"); - setup->stream = NULL; + finalize_config(setup, err); } else - posix_err = 0; - - finalize_stream_setup_errno(setup, -posix_err); + finalize_config(setup, 0); } static struct avdtp_sep_cfm cfm = { @@ -1062,10 +953,10 @@ void a2dp_exit() dbus_connection_unref(connection); } -gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) +gboolean a2dp_source_cancel(struct device *dev, unsigned int id) { - struct a2dp_stream_cb *cb_data; - struct a2dp_stream_setup *setup; + struct a2dp_setup_cb *cb_data; + struct a2dp_setup *setup; GSList *l; setup = find_setup_by_dev(dev); @@ -1073,7 +964,7 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) return FALSE; for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) { - struct a2dp_stream_cb *cb = l->data; + struct a2dp_setup_cb *cb = l->data; if (cb->id == id) { cb_data = cb; @@ -1095,17 +986,16 @@ gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id) return TRUE; } -unsigned int a2dp_source_request_stream(struct avdtp *session, - gboolean start, - a2dp_stream_cb_t cb, - void *user_data, - struct avdtp_service_capability *media_codec) +unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, + GSList *caps, void *user_data) { - struct a2dp_stream_cb *cb_data; - static unsigned int cb_id = 0; + struct a2dp_setup_cb *cb_data; GSList *l; - struct a2dp_stream_setup *setup; + struct a2dp_setup *setup; struct a2dp_sep *sep = NULL; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + int posix_err; for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; @@ -1120,91 +1010,173 @@ unsigned int a2dp_source_request_stream(struct avdtp *session, } if (!sep) { - error("a2dp_source_request_stream: no available SEP found"); + error("a2dp_source_cfg: no available SEP found"); return 0; } - setup = find_setup_by_session(session); - - debug("a2dp_source_request_stream: selected SEP %p", sep); + debug("a2dp_source_config: selected SEP %p", sep); - cb_data = g_new(struct a2dp_stream_cb, 1); - cb_data->cb = cb; + cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data->config_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; - if (setup) { - setup->canceled = FALSE; - setup->sep = sep; - setup->cb = g_slist_append(setup->cb, cb_data); - if (start) - setup->start = TRUE; - return cb_data->id; + setup = find_setup_by_session(session); + if (!setup) { + setup = g_new0(struct a2dp_setup, 1); + setup->session = avdtp_ref(session); + setups = g_slist_append(setups, setup); } - setup = g_new0(struct a2dp_stream_setup, 1); - setup->session = avdtp_ref(session); - setup->sep = sep; + setup_ref(setup); setup->cb = g_slist_append(setup->cb, cb_data); - setup->start = start; + setup->sep = sep; setup->stream = sep->stream; - setup->media_codec = media_codec; + setup->client_caps = caps; switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: - if (avdtp_discover(session, discovery_complete, setup) < 0) { - error("avdtp_discover failed"); + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, + AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + &lsep, &rsep) < 0) { + error("No matching ACP and INT SEPs found"); + goto failed; + } + + posix_err = avdtp_set_configuration(session, rsep, lsep, + caps, &setup->stream); + if (posix_err < 0) { + error("avdtp_set_configuration: %s", + strerror(-posix_err)); goto failed; } break; case AVDTP_STATE_OPEN: - if (!start) { - g_idle_add((GSourceFunc) finalize_stream_setup, setup); - break; - } - if (sep->starting) - break; - if (setup->media_codec) { - if (avdtp_stream_has_capability(setup->stream, - setup->media_codec)) { - if (avdtp_start(session, sep->stream) < 0) { - error("avdtp_start failed"); - goto failed; - } - } else { - if (avdtp_close(session, sep->stream) < 0) { - error("avdtp_close failed"); - goto failed; - } + case AVDTP_STATE_STREAMING: + if (avdtp_stream_has_capabilities(setup->stream, caps)) + g_idle_add((GSourceFunc) finalize_config, setup); + else if (!setup->reconfigure) { + setup->reconfigure = TRUE; + if (avdtp_close(session, sep->stream) < 0) { + error("avdtp_close failed"); + goto failed; } } - else if (avdtp_start(session, sep->stream) < 0) { + break; + default: + error("SEP in bad state for requesting a new stream"); + goto failed; + } + + return cb_data->id; + +failed: + setup_unref(setup); + cb_id--; + return 0; +} + +unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, + a2dp_stream_cb_t cb, void *user_data) +{ + struct a2dp_setup_cb *cb_data; + struct a2dp_setup *setup; + + cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data->resume_cb = cb; + cb_data->user_data = user_data; + cb_data->id = ++cb_id; + + setup = find_setup_by_session(session); + if (!setup) { + setup = g_new0(struct a2dp_setup, 1); + setup->session = avdtp_ref(session); + setups = g_slist_append(setups, setup); + } + + setup_ref(setup); + setup->cb = g_slist_append(setup->cb, cb_data); + setup->sep = sep; + setup->stream = sep->stream; + + switch (avdtp_sep_get_state(sep->sep)) { + case AVDTP_STATE_IDLE: + goto failed; + break; + case AVDTP_STATE_OPEN: + if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } break; case AVDTP_STATE_STREAMING: - if (!start || !sep->suspending) { - if (sep->suspend_timer) { - g_source_remove(sep->suspend_timer); - sep->suspend_timer = 0; - avdtp_unref(sep->session); - sep->session = NULL; - } - g_idle_add((GSourceFunc) finalize_stream_setup, setup); + if (!sep->suspending && sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; + avdtp_unref(sep->session); + sep->session = NULL; } + g_idle_add((GSourceFunc) finalize_resume, setup); break; default: - error("SEP in bad state for requesting a new stream"); + error("SEP in bad state"); goto failed; } - setups = g_slist_append(setups, setup); + return cb_data->id; + +failed: + setup_unref(setup); + cb_id--; + return 0; +} + +unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, + a2dp_stream_cb_t cb, void *user_data) +{ + struct a2dp_setup_cb *cb_data; + struct a2dp_setup *setup; + + cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data->suspend_cb = cb; + cb_data->user_data = user_data; + cb_data->id = ++cb_id; + + setup = find_setup_by_session(session); + if (!setup) { + setup = g_new0(struct a2dp_setup, 1); + setup->session = avdtp_ref(session); + setups = g_slist_append(setups, setup); + } + + setup_ref(setup); + setup->cb = g_slist_append(setup->cb, cb_data); + setup->sep = sep; + setup->stream = sep->stream; + + switch (avdtp_sep_get_state(sep->sep)) { + case AVDTP_STATE_IDLE: + error("a2dp_source_suspend: no stream to suspend"); + goto failed; + break; + case AVDTP_STATE_OPEN: + g_idle_add((GSourceFunc) finalize_suspend, setup); + break; + case AVDTP_STATE_STREAMING: + if (avdtp_start(session, sep->stream) < 0) { + error("avdtp_start failed"); + goto failed; + } + break; + default: + error("SEP in bad state for resume"); + goto failed; + } return cb_data->id; failed: - stream_setup_free(setup); + setup_unref(setup); cb_id--; return 0; } @@ -1248,67 +1220,3 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) return TRUE; } -gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session) -{ - avdtp_state_t state; - GSList *l; - struct a2dp_sep *sep = NULL; - - for (l = sources; l != NULL; l = l->next) { - struct a2dp_sep *tmp = l->data; - - if (tmp->session && tmp->session == session) { - sep = tmp; - break; - } - } - - if (!sep) - return FALSE; - - state = avdtp_sep_get_state(sep->sep); - - if (!sep->stream || state != AVDTP_STATE_STREAMING) - return TRUE; - - if (avdtp_suspend(session, sep->stream) == 0) { - sep->suspending = TRUE; - return TRUE; - } - - return FALSE; -} - -gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session) -{ - avdtp_state_t state; - GSList *l; - struct a2dp_sep *sep = NULL; - - for (l = sources; l != NULL; l = l->next) { - struct a2dp_sep *tmp = l->data; - - if (tmp->session && tmp->session == session) { - sep = tmp; - break; - } - } - - if (!sep) - return FALSE; - - state = avdtp_sep_get_state(sep->sep); - - if (state < AVDTP_STATE_OPEN) { - error("a2dp_source_start_stream: no stream open"); - return FALSE; - } - - if (state == AVDTP_STATE_STREAMING) - return TRUE; - - if (avdtp_start(session, sep->stream) < 0) - return FALSE; - - return TRUE; -} diff --git a/audio/a2dp.h b/audio/a2dp.h index 6579f64c..8227296f 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -48,6 +48,9 @@ #define A2DP_ALLOCATION_SNR (1 << 1) #define A2DP_ALLOCATION_LOUDNESS 1 +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2 + #if __BYTE_ORDER == __LITTLE_ENDIAN struct sbc_codec_cap { @@ -80,21 +83,24 @@ struct sbc_codec_cap { struct a2dp_sep; -typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct a2dp_sep *sep, +typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, - void *user_data, - struct avdtp_error *err); + struct avdtp_error *err, + void *user_data); +typedef void (*a2dp_stream_cb_t) (struct avdtp *session, + struct avdtp_error *err, + void *user_data); int a2dp_init(DBusConnection *conn, int sources, int sinks); void a2dp_exit(void); -unsigned int a2dp_source_request_stream(struct avdtp *session, - gboolean start, a2dp_stream_cb_t cb, - void *user_data, - struct avdtp_service_capability *media_codec); -gboolean a2dp_source_cancel_stream(struct device *dev, unsigned int id); +unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, + GSList *caps, void *user_data); +unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, + a2dp_stream_cb_t cb, void *user_data); +unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, + a2dp_stream_cb_t cb, void *user_data); +gboolean a2dp_source_cancel(struct device *dev, unsigned int id); gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session); gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session); -gboolean a2dp_source_suspend(struct device *dev, struct avdtp *session); -gboolean a2dp_source_start_stream(struct device *dev, struct avdtp *session); diff --git a/audio/avdtp.c b/audio/avdtp.c index f68561e9..3acd4128 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1825,16 +1825,20 @@ static gboolean avdtp_discover_resp(struct avdtp *session, resp->seps[i].media_type, resp->seps[i].inuse); /* Skip SEP's which are in use */ +/* if (resp->seps[i].inuse) continue; +*/ sep = find_remote_sep(session->seps, resp->seps[i].seid); if (!sep) { sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); } +/* else if (sep && sep->stream) continue; +*/ sep->seid = resp->seps[i].seid; sep->type = resp->seps[i].type; @@ -2275,6 +2279,21 @@ gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, return FALSE; } +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps) +{ + GSList *l; + + for (l = caps; l; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (!avdtp_stream_has_capability(stream, cap)) + return FALSE; + } + + return TRUE; +} + gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, uint16_t *imtu, uint16_t *omtu, GSList **caps) @@ -2331,6 +2350,9 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, { struct avdtp_service_capability *cap; + if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_MEDIA_CODEC) + return NULL; + cap = g_malloc(sizeof(struct avdtp_service_capability) + length); cap->category = category; cap->length = length; @@ -2457,6 +2479,18 @@ int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) return send_request(session, FALSE, stream, &req, sizeof(req)); } +static void copy_capabilities(gpointer data, gpointer user_data) +{ + struct avdtp_service_capability *src_cap = data; + struct avdtp_service_capability *dst_cap; + GSList **l = user_data; + + dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, + src_cap->length); + + *l = g_slist_append(*l, dst_cap); +} + int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, struct avdtp_local_sep *lsep, @@ -2484,7 +2518,8 @@ int avdtp_set_configuration(struct avdtp *session, new_stream->session = session; new_stream->lsep = lsep; new_stream->rseid = rsep->seid; - new_stream->caps = caps; + + g_slist_foreach(caps, copy_capabilities, &new_stream->caps); /* Calculate total size of request */ for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { diff --git a/audio/avdtp.h b/audio/avdtp.h index 88684f28..241fa713 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -221,6 +221,8 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, struct avdtp_service_capability *cap); +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps); int avdtp_set_configuration(struct avdtp *session, struct avdtp_remote_sep *rsep, diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index b24dc8b8..1b893cc1 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -45,6 +45,7 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); #define GST_CAT_DEFAULT a2dp_sink_debug #define BUFFER_SIZE 2048 +#define TEMPLATE_MAX_BITPOOL_VALUE 64 #define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START { \ g_mutex_lock (s->sink_lock); \ @@ -56,8 +57,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); struct bluetooth_data { - struct bt_getcapabilities_rsp cfg; /* Bluetooth device config */ - gint link_mtu; + struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */ + struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */ int samples; /* Number of encoded samples */ gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ gsize count; /* Codec transfer buffer counter */ @@ -67,6 +68,7 @@ struct bluetooth_data { int frame_count; /* Current frames in buffer*/ }; + #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) #define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) @@ -92,6 +94,7 @@ static GstStaticPadTemplate a2dp_sink_factory = "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }," + /* FIXME use constant here */ "bitpool = (int) [ 2, 64 ]; " "audio/mpeg, " "mpegversion = (int) 1, " @@ -230,9 +233,10 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, GstCaps *caps, sbc_capabilities_t *pkt) { - sbc_capabilities_t *cfg = &sink->data->cfg.sbc_capabilities; + sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; const GValue *value = NULL; const char *pref, *name; + gint rate, blocks, subbands; GstStructure *structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name(structure); @@ -243,7 +247,19 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, } value = gst_structure_get_value(structure, "rate"); - cfg->frequency = g_value_get_int(value); + rate = g_value_get_int(value); + if (rate == 44100) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; + else if (rate == 48000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; + else if (rate == 32000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; + else if (rate == 16000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); + return FALSE; + } value = gst_structure_get_value(structure, "mode"); pref = g_value_get_string(value); @@ -276,13 +292,34 @@ static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, } value = gst_structure_get_value(structure, "subbands"); - cfg->subbands = g_value_get_int(value); + subbands = g_value_get_int(value); + if (subbands == 8) + cfg->subbands = BT_A2DP_SUBBANDS_8; + else if (subbands == 4) + cfg->subbands = BT_A2DP_SUBBANDS_4; + else { + GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); + return FALSE; + } value = gst_structure_get_value(structure, "blocks"); - cfg->block_length = g_value_get_int(value); + blocks = g_value_get_int(value); + if (blocks == 16) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (blocks == 12) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (blocks == 8) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (blocks == 4) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); + return FALSE; + } /* FIXME min and max ??? */ value = gst_structure_get_value(structure, "bitpool"); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); memcpy(pkt, cfg, sizeof(*pkt)); @@ -375,7 +412,7 @@ static gboolean server_callback(GIOChannel *chan, static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) { - sbc_capabilities_t *sbc = &self->data->cfg.sbc_capabilities; + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; GstStructure *structure; GValue *value; GValue *list; @@ -519,7 +556,9 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) /* bitpool */ value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, sbc->min_bitpool, sbc->max_bitpool); + gst_value_set_int_range(value, + MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE), + MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE)); gst_structure_set_value(structure, "bitpool", value); /* channels */ @@ -528,6 +567,8 @@ static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) g_free(value); + if (self->dev_caps != NULL) + gst_caps_unref(self->dev_caps); self->dev_caps = gst_caps_new_full(structure, NULL); tmp = gst_caps_to_string(self->dev_caps); @@ -549,8 +590,6 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) req->h.msg_type = BT_GETCAPABILITIES_REQ; strncpy(req->device, self->device, 18); - req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; - req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; io_error = gst_a2dp_sink_audioservice_send(self, &req->h); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while asking device caps"); @@ -570,12 +609,7 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) return FALSE; } - if (rsp->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { - GST_ERROR_OBJECT(self, "Non a2dp answer from device"); - return FALSE; - } - - memcpy(&self->data->cfg, rsp, sizeof(*rsp)); + memcpy(&self->data->caps, rsp, sizeof(*rsp)); if (!gst_a2dp_sink_update_caps(self)) { GST_WARNING_OBJECT(self, "failed to update capabilities"); return FALSE; @@ -685,6 +719,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) memset (req, 0, sizeof(buf)); req->h.msg_type = BT_SETCONFIGURATION_REQ; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; strncpy(req->device, self->device, 18); ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); if (!ret) { @@ -716,6 +751,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) return FALSE; } + memcpy(&self->data->cfg, rsp, sizeof(*rsp)); GST_DEBUG_OBJECT(self, "configuration set"); return TRUE; @@ -801,7 +837,10 @@ static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) { GstA2dpSink *self = GST_A2DP_SINK(basesink); - return self->dev_caps ? gst_caps_ref(self->dev_caps): NULL; + if (self->dev_caps) + return gst_caps_ref(self->dev_caps); + + return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self))); } static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) diff --git a/audio/ipc.h b/audio/ipc.h index 0384cfd6..096c29f1 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -115,7 +115,6 @@ struct bt_getcapabilities_req { bt_audio_msg_header_t h; char device[18]; /* Address of the remote Device */ uint8_t transport; /* Requested transport */ - uint8_t access_mode; /* Requested access mode */ } __attribute__ ((packed)); /* BT_GETCAPABILITIES_RSP */ @@ -165,8 +164,6 @@ struct bt_getcapabilities_rsp { bt_audio_msg_header_t h; uint8_t posix_errno; uint8_t transport; /* Granted transport */ - uint8_t access_mode; /* Granted access mode */ - uint16_t link_mtu; /* Max length that transport supports */ sbc_capabilities_t sbc_capabilities; /* A2DP only */ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ uint16_t sampling_rate; /* SCO only */ @@ -176,6 +173,8 @@ struct bt_getcapabilities_rsp { struct bt_setconfiguration_req { bt_audio_msg_header_t h; char device[18]; /* Address of the remote Device */ + uint8_t transport; /* Requested transport */ + uint8_t access_mode; /* Requested access mode */ sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field and next one must be filled */ mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ @@ -184,6 +183,9 @@ struct bt_setconfiguration_req { /* BT_SETCONFIGURATION_RSP */ struct bt_setconfiguration_rsp { bt_audio_msg_header_t h; + uint8_t transport; /* Granted transport */ + uint8_t access_mode; /* Granted access mode */ + uint16_t link_mtu; /* Max length that transport supports */ uint8_t posix_errno; } __attribute__ ((packed)); diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a5bfcdf9..a177274a 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -69,6 +69,17 @@ #define SCO_RXBUFS 0x04 #endif +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#define MAX_BITPOOL 64 +#define MIN_BITPOOL 2 + /* adapted from glibc sys/time.h timersub() macro */ #define priv_timespecsub(a, b, result) \ do { \ @@ -411,6 +422,120 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) return write(data->pipefd[1], &c, 1); } +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) +{ + switch (freq) { + case BT_A2DP_SAMPLING_FREQ_16000: + case BT_A2DP_SAMPLING_FREQ_32000: + return 53; + case BT_A2DP_SAMPLING_FREQ_44100: + switch (mode) { + case BT_A2DP_CHANNEL_MODE_MONO: + case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 31; + case BT_A2DP_CHANNEL_MODE_STEREO: + case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: + return 53; + default: + DBG("Invalid channel mode %u", mode); + return 53; + } + case BT_A2DP_SAMPLING_FREQ_48000: + switch (mode) { + case BT_A2DP_CHANNEL_MODE_MONO: + case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 29; + case BT_A2DP_CHANNEL_MODE_STEREO: + case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: + return 51; + default: + DBG("Invalid channel mode %u", mode); + return 51; + } + default: + DBG("Invalid sampling freq %u", freq); + return 53; + } +} + +static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, + unsigned int channels) +{ + unsigned int max_bitpool, min_bitpool; + + switch (rate) { + case 48000: + cap->frequency = BT_A2DP_SAMPLING_FREQ_48000; + break; + case 44100: + cap->frequency = BT_A2DP_SAMPLING_FREQ_44100; + break; + case 32000: + cap->frequency = BT_A2DP_SAMPLING_FREQ_32000; + break; + case 16000: + cap->frequency = BT_A2DP_SAMPLING_FREQ_16000; + break; + default: + DBG("Rate %d not supported", rate); + return -1; + } + + if (channels == 2) { + if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + } else { + if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + } + + if (!cap->channel_mode) { + DBG("No supported channel modes"); + return -1; + } + + if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) + cap->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) + cap->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) + cap->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) + cap->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + DBG("No supported block lengths"); + return -1; + } + + if (cap->subbands & BT_A2DP_SUBBANDS_8) + cap->subbands = BT_A2DP_SUBBANDS_8; + else if (cap->subbands & BT_A2DP_SUBBANDS_4) + cap->subbands = BT_A2DP_SUBBANDS_4; + else { + DBG("No supported subbands"); + return -1; + } + + if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) + cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) + cap->allocation_method = BT_A2DP_ALLOCATION_SNR; + + min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool); + max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + cap->max_bitpool); + + cap->min_bitpool = min_bitpool; + cap->max_bitpool = max_bitpool; + + return 0; +} + + static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { @@ -419,7 +544,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, char buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_setconfiguration_req *setconf_req = (void*) buf; struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; - int err; + unsigned int rate, channels; + int err, dir; sbc_capabilities_t active_capabilities; DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", @@ -428,11 +554,20 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, /* FIXME: this needs to be really implemented (take into account real asoundrc settings + ALSA hw settings ) once server side sends us more than one possible configuration */ + snd_pcm_hw_params_get_rate(params, &rate, &dir); + snd_pcm_hw_params_get_channels(params, &channels); + err = select_sbc_params(&a2dp->sbc_capabilities, rate, channels); + if (err < 0) + return err; + active_capabilities = a2dp->sbc_capabilities; memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; setconf_req->sbc_capabilities = active_capabilities; + setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? + BT_CAPABILITIES_ACCESS_MODE_WRITE : + BT_CAPABILITIES_ACCESS_MODE_READ); err = audioservice_send(data->server.fd, &setconf_req->h); if (err < 0) @@ -450,13 +585,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, return -setconf_rsp->posix_errno; } + data->transport = setconf_rsp->transport; + data->link_mtu = setconf_rsp->link_mtu; + /* Setup SBC encoder now we agree on parameters */ if (a2dp->sbc_initialized) - sbc_finish(&a2dp->sbc); - - /* FIXME: init using flags? */ - sbc_init(&a2dp->sbc, 0); + sbc_reinit(&a2dp->sbc, 0); + else + sbc_init(&a2dp->sbc, 0); a2dp->sbc_initialized = 1; + if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) a2dp->sbc.rate = 16000; @@ -1327,11 +1465,11 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, getcaps_req->transport = alsa_conf->transport; else getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY; - +/* getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ? BT_CAPABILITIES_ACCESS_MODE_WRITE : BT_CAPABILITIES_ACCESS_MODE_READ); - +*/ err = audioservice_send(data->server.fd, &getcaps_req->h); if (err < 0) goto failed; @@ -1348,7 +1486,9 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, } data->transport = getcaps_rsp->transport; +/* data->link_mtu = getcaps_rsp->link_mtu; +*/ if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP) data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities; diff --git a/audio/sink.c b/audio/sink.c index b1fc5b2b..259abf8f 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -159,7 +159,7 @@ static gboolean stream_setup_retry(gpointer user_data) static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, - void *user_data, struct avdtp_error *err) + struct avdtp_error *err, void *user_data) { struct sink *sink = user_data; struct pending_request *pending; @@ -190,23 +190,212 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, } } +static uint8_t default_bitpool(uint8_t freq, uint8_t mode) +{ + switch (freq) { + case A2DP_SAMPLING_FREQ_16000: + case A2DP_SAMPLING_FREQ_32000: + return 53; + case A2DP_SAMPLING_FREQ_44100: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 31; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 53; + default: + error("Invalid channel mode %u", mode); + return 53; + } + case A2DP_SAMPLING_FREQ_48000: + switch (mode) { + case A2DP_CHANNEL_MODE_MONO: + case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + return 29; + case A2DP_CHANNEL_MODE_STEREO: + case A2DP_CHANNEL_MODE_JOINT_STEREO: + return 51; + default: + error("Invalid channel mode %u", mode); + return 51; + } + default: + error("Invalid sampling freq %u", freq); + return 53; + } +} + +static gboolean select_sbc_params(struct sbc_codec_cap *cap, + struct sbc_codec_cap *supported) +{ + unsigned int max_bitpool, min_bitpool; + + memset(cap, 0, sizeof(struct sbc_codec_cap)); + + cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + cap->cap.media_codec_type = A2DP_CODEC_SBC; + + if (supported->frequency & A2DP_SAMPLING_FREQ_44100) + cap->frequency = A2DP_SAMPLING_FREQ_44100; + else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) + cap->frequency = A2DP_SAMPLING_FREQ_48000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) + cap->frequency = A2DP_SAMPLING_FREQ_32000; + else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) + cap->frequency = A2DP_SAMPLING_FREQ_16000; + else { + error("No supported frequencies"); + return FALSE; + } + + if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) + cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) + cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + else { + error("No supported channel modes"); + return FALSE; + } + + if (supported->block_length & A2DP_BLOCK_LENGTH_16) + cap->block_length = A2DP_BLOCK_LENGTH_16; + else if (supported->block_length & A2DP_BLOCK_LENGTH_12) + cap->block_length = A2DP_BLOCK_LENGTH_12; + else if (supported->block_length & A2DP_BLOCK_LENGTH_8) + cap->block_length = A2DP_BLOCK_LENGTH_8; + else if (supported->block_length & A2DP_BLOCK_LENGTH_4) + cap->block_length = A2DP_BLOCK_LENGTH_4; + else { + error("No supported block lengths"); + return FALSE; + } + + if (supported->subbands & A2DP_SUBBANDS_8) + cap->subbands = A2DP_SUBBANDS_8; + else if (supported->subbands & A2DP_SUBBANDS_4) + cap->subbands = A2DP_SUBBANDS_4; + else { + error("No supported subbands"); + return FALSE; + } + + if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) + cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; + else if (supported->allocation_method & A2DP_ALLOCATION_SNR) + cap->allocation_method = A2DP_ALLOCATION_SNR; + + min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); + max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), + supported->max_bitpool); + + cap->min_bitpool = min_bitpool; + cap->max_bitpool = max_bitpool; + + return TRUE; +} + +static gboolean select_capabilities(struct avdtp *session, + struct avdtp_remote_sep *rsep, + GSList **caps) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap; + + media_codec = avdtp_get_codec(rsep); + if (!media_codec) + return FALSE; + + select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data); + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + *caps = g_slist_append(*caps, media_codec); + + + return TRUE; +} + +static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, + void *user_data) +{ + struct sink *sink = user_data; + struct pending_request *pending; + struct avdtp_local_sep *lsep; + struct avdtp_remote_sep *rsep; + GSList *caps = NULL; + int id; + + pending = sink->connect; + + if (err) { + avdtp_unref(sink->session); + sink->session = NULL; + if (avdtp_error_type(err) == AVDTP_ERROR_ERRNO + && avdtp_error_posix_errno(err) != EHOSTDOWN) { + debug("connect:connect XCASE detected"); + g_timeout_add(STREAM_SETUP_RETRY_TIMER, + stream_setup_retry, sink); + } else + goto failed; + return; + } + + debug("Discovery complete"); + + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + A2DP_CODEC_SBC, &lsep, &rsep) < 0) { + error("No matching ACP and INT SEPs found"); + goto failed; + } + + if (!select_capabilities(session, rsep, &caps)) { + error("Unable to select remote SEP capabilities"); + goto failed; + } + + id = a2dp_source_config(sink->session, stream_setup_complete, + caps, sink); + if (id == 0) + goto failed; + + pending->id = id; + return; + +failed: + pending_request_free(pending); + sink->connect = NULL; + avdtp_unref(sink->session); + sink->session = NULL; + error_failed(pending->conn, pending->msg, "Stream setup failed"); +} + static DBusHandlerResult sink_connect(DBusConnection *conn, DBusMessage *msg, void *data) { struct device *dev = data; struct sink *sink = dev->sink; struct pending_request *pending; - unsigned int id; if (!sink->session) sink->session = avdtp_get(&dev->src, &dev->dst); if (!sink->session) - return error_failed(conn, msg, - "Unable to get a session"); + return error_failed(conn, msg, "Unable to get a session"); if (sink->connect || sink->disconnect) - return error_in_progress(conn, msg, "Device connection already in progress"); + return error_in_progress(conn, msg, "Device connection" + "already in progress"); if (sink->state >= AVDTP_STATE_OPEN) return error_already_connected(conn, msg); @@ -216,21 +405,10 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, pending->msg = dbus_message_ref(msg); sink->connect = pending; - id = a2dp_source_request_stream(sink->session, FALSE, - stream_setup_complete, sink, - NULL); - if (id == 0) { - pending_request_free(pending); - sink->connect = NULL; - avdtp_unref(sink->session); - sink->session = NULL; - return error_failed(conn, msg, "Failed to request a stream"); - } + avdtp_discover(sink->session, discovery_complete, sink); debug("stream creation in progress"); - pending->id = id; - return DBUS_HANDLER_RESULT_HANDLED; } diff --git a/audio/unix.c b/audio/unix.c index ea96bd4f..c321b99b 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -71,7 +71,7 @@ struct headset_data { struct unix_client { struct device *dev; - struct avdtp_service_capability *media_codec; + GSList *caps; service_type_t type; char *interface; union { @@ -83,18 +83,13 @@ struct unix_client { int data_fd; /* To be deleted once two phase configuration is fully implemented */ unsigned int req_id; unsigned int cb_id; - gboolean (*cancel_stream) (struct device *dev, unsigned int id); + gboolean (*cancel) (struct device *dev, unsigned int id); }; static GSList *clients = NULL; static int unix_sock = -1; -static void unix_ipc_sendmsg(struct unix_client *client, - const bt_audio_msg_header_t *msg); - -static void send_getcapabilities_rsp_error(struct unix_client *client, int err); - static void client_free(struct unix_client *client) { struct a2dp_data *a2dp; @@ -118,8 +113,10 @@ static void client_free(struct unix_client *client) if (client->sock >= 0) close(client->sock); - if (client->media_codec) - g_free(client->media_codec); + if (client->caps) { + g_slist_foreach(client->caps, (GFunc) g_free, NULL); + g_slist_free(client->caps); + } g_free(client->interface); g_free(client); @@ -152,6 +149,27 @@ static int unix_sendmsg_fd(int sock, int fd) return sendmsg(sock, &msgh, MSG_NOSIGNAL); } +static void unix_ipc_sendmsg(struct unix_client *client, + const bt_audio_msg_header_t *msg) +{ + info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); + + if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) + error("Error %s(%d)", strerror(errno), errno); +} + +static void unix_ipc_error(struct unix_client *client, int type, int err) +{ + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = type; + rsp->posix_errno = err; + + unix_ipc_sendmsg(client, &rsp->h); +} + static service_type_t select_service(struct device *dev, const char *interface) { if (!interface) { @@ -198,8 +216,8 @@ static void stream_state_changed(struct avdtp_stream *stream, break; } } - -static void headset_setup_complete(struct device *dev, void *user_data) +/* +static void headset_discovery_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -209,7 +227,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) client->req_id = 0; if (!dev) { - send_getcapabilities_rsp_error(client, EIO); + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); client->dev = NULL; return; } @@ -231,7 +249,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) if (!headset_lock(dev, hs->lock)) { error("Unable to lock headset"); - send_getcapabilities_rsp_error(client, EIO); + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); client->dev = NULL; return; } @@ -240,25 +258,131 @@ static void headset_setup_complete(struct device *dev, void *user_data) rsp->h.msg_type = BT_GETCAPABILITIES_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; - rsp->access_mode = client->access_mode; - rsp->link_mtu = 48; rsp->sampling_rate = 8000; client->data_fd = headset_get_sco_fd(dev); unix_ipc_sendmsg(client, &rsp->h); } +*/ +static void headset_setup_complete(struct device *dev, void *user_data) +{ + struct unix_client *client = user_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + struct headset_data *hs = &client->d.hs; -static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - void *user_data, struct avdtp_error *err) + client->req_id = 0; + + if (!dev) { + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); + client->dev = NULL; + return; + } + + switch (client->access_mode) { + case BT_CAPABILITIES_ACCESS_MODE_READ: + hs->lock = HEADSET_LOCK_READ; + break; + case BT_CAPABILITIES_ACCESS_MODE_WRITE: + hs->lock = HEADSET_LOCK_WRITE; + break; + case BT_CAPABILITIES_ACCESS_MODE_READWRITE: + hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; + break; + default: + hs->lock = 0; + break; + } + + if (!headset_lock(dev, hs->lock)) { + error("Unable to lock headset"); + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); + client->dev = NULL; + return; + } + + memset(buf, 0, sizeof(buf)); + + rsp->h.msg_type = BT_SETCONFIGURATION_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; + rsp->access_mode = client->access_mode; + + client->data_fd = headset_get_sco_fd(dev); + + unix_ipc_sendmsg(client, &rsp->h); +} + +static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, + struct avdtp_error *err, + void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_getcapabilities_rsp *rsp = (void *) buf; - struct avdtp_service_capability *cap; - struct avdtp_media_codec_capability *codec_cap; - struct sbc_codec_cap *sbc_cap; + struct a2dp_data *a2dp = &client->d.a2dp; + struct sbc_codec_cap *sbc_cap = NULL; + GSList *l; + + if (err) + goto failed; + + memset(buf, 0, sizeof(buf)); + client->req_id = 0; + + rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + + for (l = seps; l; l = g_slist_next(l)) { + struct avdtp_remote_sep *rsep = l->data; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap; + + cap = avdtp_get_codec(rsep); + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec_cap = (void *) cap->data; + + if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) + sbc_cap = (void *) codec_cap; + + } + + /* endianess prevent direct cast */ + if (sbc_cap) { + rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; + rsp->sbc_capabilities.frequency = sbc_cap->frequency; + rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; + rsp->sbc_capabilities.subbands = sbc_cap->subbands; + rsp->sbc_capabilities.block_length = sbc_cap->block_length; + rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; + rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; + } + + unix_ipc_sendmsg(client, &rsp->h); + + return; + +failed: + error("discovery failed"); + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); + + avdtp_unref(a2dp->session); + + a2dp->session = NULL; + a2dp->stream = NULL; +} + +static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data) +{ + struct unix_client *client = user_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_rsp *rsp = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; uint16_t imtu, omtu; GSList *caps; @@ -282,40 +406,13 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, goto failed; } - for (codec_cap = NULL; caps; caps = g_slist_next(caps)) { - cap = caps->data; - if (cap->category == AVDTP_MEDIA_CODEC) { - codec_cap = (void *) cap->data; - break; - } - } - - if (codec_cap == NULL || - codec_cap->media_codec_type != A2DP_CODEC_SBC) { - error("Unable to find matching codec capability"); - goto failed; - } - - rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->h.msg_type = BT_SETCONFIGURATION_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; rsp->access_mode = client->access_mode; /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ rsp->link_mtu = omtu; - sbc_cap = (void *) codec_cap; - - /* assignations below are ok as soon as newipc.h and a2dp.h are kept */ - /* in sync. However it is not possible to cast a struct to another */ - /* dues to endianess issues */ - rsp->sbc_capabilities.channel_mode = sbc_cap->channel_mode; - rsp->sbc_capabilities.frequency = sbc_cap->frequency; - rsp->sbc_capabilities.allocation_method = sbc_cap->allocation_method; - rsp->sbc_capabilities.subbands = sbc_cap->subbands; - rsp->sbc_capabilities.block_length = sbc_cap->block_length; - rsp->sbc_capabilities.min_bitpool = sbc_cap->min_bitpool; - rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; - unix_ipc_sendmsg(client, &rsp->h); client->cb_id = avdtp_stream_add_cb(session, stream, @@ -324,12 +421,86 @@ static void a2dp_setup_complete(struct avdtp *session, struct a2dp_sep *sep, return; failed: - error("stream setup failed"); + error("setup failed"); + + if (a2dp->sep) { + a2dp_sep_unlock(a2dp->sep, a2dp->session); + a2dp->sep = NULL; + } + unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); + + avdtp_unref(a2dp->session); + + a2dp->session = NULL; + a2dp->stream = NULL; +} + +static void a2dp_resume_complete(struct avdtp *session, + struct avdtp_error *err, void *user_data) +{ + struct unix_client *client = user_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_datafd_ind *ind = (void *) buf; + struct a2dp_data *a2dp = &client->d.a2dp; + + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTART_RSP; + rsp->posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->h); + + memset(buf, 0, sizeof(buf)); + ind->h.msg_type = BT_STREAMFD_IND; + unix_ipc_sendmsg(client, &ind->h); + + if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { + error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); + goto failed; + } + + return; + +failed: + error("resume failed"); + + if (a2dp->sep) { + a2dp_sep_unlock(a2dp->sep, a2dp->session); + a2dp->sep = NULL; + } + unix_ipc_error(client, BT_STREAMSTART_REQ, EIO); + + avdtp_unref(a2dp->session); + + a2dp->session = NULL; + a2dp->stream = NULL; +} + +static void a2dp_suspend_complete(struct avdtp *session, + struct avdtp_error *err, void *user_data) +{ + struct unix_client *client = user_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct a2dp_data *a2dp = &client->d.a2dp; + + if (err) + goto failed; + + memset(buf, 0, sizeof(buf)); + rsp->h.msg_type = BT_STREAMSTOP_RSP; + rsp->posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->h); + + return; + +failed: + error("suspend failed"); + if (a2dp->sep) { a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } - send_getcapabilities_rsp_error(client, EIO); + unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO); avdtp_unref(a2dp->session); @@ -337,10 +508,11 @@ failed: a2dp->stream = NULL; } -static void create_stream(struct device *dev, struct unix_client *client) +static void start_discovery(struct device *dev, struct unix_client *client) { struct a2dp_data *a2dp; unsigned int id; + int err = 0; client->type = select_service(dev, client->interface); @@ -356,18 +528,55 @@ static void create_stream(struct device *dev, struct unix_client *client) goto failed; } - /* FIXME: The provided media_codec breaks bitpool - selection. So disable it. This needs fixing */ - id = a2dp_source_request_stream(a2dp->session, - TRUE, a2dp_setup_complete, - client, - NULL/*client->media_codec*/); - client->cancel_stream = a2dp_source_cancel_stream; + err = avdtp_discover(a2dp->session, a2dp_discovery_complete, + client); + if (err) + goto failed; break; case TYPE_HEADSET: id = headset_request_stream(dev, headset_setup_complete, client); - client->cancel_stream = headset_cancel_stream; + client->cancel = headset_cancel_stream; + break; + + default: + error("No known services for device"); + goto failed; + } + + return; + +failed: + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, err ? : EIO); +} + +static void start_config(struct device *dev, struct unix_client *client) +{ + struct a2dp_data *a2dp; + unsigned int id; + + client->type = select_service(dev, client->interface); + + switch (client->type) { + case TYPE_SINK: + a2dp = &client->d.a2dp; + + if (!a2dp->session) + a2dp->session = avdtp_get(&dev->src, &dev->dst); + + if (!a2dp->session) { + error("Unable to get a session"); + goto failed; + } + + id = a2dp_source_config(a2dp->session, a2dp_config_complete, + client->caps, client); + client->cancel = a2dp_source_cancel; + break; + + case TYPE_HEADSET: + id = headset_request_stream(dev, headset_setup_complete, client); + client->cancel = headset_cancel_stream; break; default: @@ -376,7 +585,7 @@ static void create_stream(struct device *dev, struct unix_client *client) } if (id == 0) { - error("request_stream failed"); + error("config failed"); goto failed; } @@ -386,37 +595,117 @@ static void create_stream(struct device *dev, struct unix_client *client) return; failed: - send_getcapabilities_rsp_error(client, EIO); + unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); } -static void create_cb(struct device *dev, void *user_data) +static void start_resume(struct device *dev, struct unix_client *client) { - struct unix_client *client = user_data; + struct a2dp_data *a2dp; + unsigned int id; - if (!dev) - send_getcapabilities_rsp_error(client, EIO); - else - create_stream(dev, client); + client->type = select_service(dev, client->interface); + + switch (client->type) { + case TYPE_SINK: + a2dp = &client->d.a2dp; + + if (!a2dp->session) + a2dp->session = avdtp_get(&dev->src, &dev->dst); + + if (!a2dp->session) { + error("Unable to get a session"); + goto failed; + } + + if (!a2dp->sep) { + error("Unable to get a sep"); + goto failed; + } + + id = a2dp_source_resume(a2dp->session, a2dp->sep, + a2dp_resume_complete, client); + client->cancel = a2dp_source_cancel; + break; + + case TYPE_HEADSET: + id = headset_request_stream(dev, headset_setup_complete, client); + client->cancel = headset_cancel_stream; + break; + + default: + error("No known services for device"); + goto failed; + } + + if (id == 0) { + error("resume failed"); + goto failed; + } + + return; + +failed: + unix_ipc_error(client, BT_STREAMSTART_RSP, EIO); } -static void unix_ipc_sendmsg(struct unix_client *client, - const bt_audio_msg_header_t *msg) +static void start_suspend(struct device *dev, struct unix_client *client) { - info("Audio API: sending %s", bt_audio_strmsg(msg->msg_type)); - if (send(client->sock, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) < 0) - error("Error %s(%d)", strerror(errno), errno); + struct a2dp_data *a2dp; + unsigned int id; + + client->type = select_service(dev, client->interface); + + switch (client->type) { + case TYPE_SINK: + a2dp = &client->d.a2dp; + + if (!a2dp->session) + a2dp->session = avdtp_get(&dev->src, &dev->dst); + + if (!a2dp->session) { + error("Unable to get a session"); + goto failed; + } + + if (!a2dp->sep) { + error("Unable to get a sep"); + goto failed; + } + + id = a2dp_source_suspend(a2dp->session, a2dp->sep, + a2dp_suspend_complete, client); + client->cancel = a2dp_source_cancel; + break; + + case TYPE_HEADSET: + id = headset_request_stream(dev, headset_setup_complete, client); + client->cancel = headset_cancel_stream; + break; + + default: + error("No known services for device"); + goto failed; + } + + if (id == 0) { + error("suspend failed"); + goto failed; + } + + return; + +failed: + unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO); } -static void send_getcapabilities_rsp_error(struct unix_client *client, int err) +static void create_cb(struct device *dev, void *user_data) { - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_getcapabilities_rsp *rsp = (void *) buf; - - memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_GETCAPABILITIES_RSP; - rsp->posix_errno = err; + struct unix_client *client = user_data; - unix_ipc_sendmsg(client, &rsp->h); + if (!dev) + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); + else + start_discovery(dev, client); } static void handle_getcapabilities_req(struct unix_client *client, @@ -427,13 +716,6 @@ static void handle_getcapabilities_req(struct unix_client *client, str2ba(req->device, &bdaddr); - if (!req->access_mode) { - send_getcapabilities_rsp_error(client, EINVAL); - return; - } - - client->access_mode = req->access_mode; - if (client->interface) { g_free(client->interface); client->interface = NULL; @@ -444,8 +726,6 @@ static void handle_getcapabilities_req(struct unix_client *client, else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) client->interface = g_strdup(AUDIO_SINK_INTERFACE); - client->media_codec = 0; - if (!manager_find_device(&bdaddr, NULL, FALSE)) { if (!bacmp(&bdaddr, BDADDR_ANY)) goto failed; @@ -461,63 +741,118 @@ static void handle_getcapabilities_req(struct unix_client *client, if (!dev) goto failed; - create_stream(dev, client); + start_discovery(dev, client); return; failed: - send_getcapabilities_rsp_error(client, EIO); + unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); } static void handle_setconfiguration_req(struct unix_client *client, struct bt_setconfiguration_req *req) { - /* FIXME: for now we just blindly assume that we receive is the - only valid configuration sent.*/ - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_setconfiguration_rsp *rsp = (void *) buf; + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap; + struct device *dev; + bdaddr_t bdaddr; + int err = 0; - memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_SETCONFIGURATION_RSP; - rsp->posix_errno = 0; + if (!req->access_mode) { + err = EINVAL; + goto failed; + } - unix_ipc_sendmsg(client, &rsp->h); + str2ba(req->device, &bdaddr); + + if (client->interface) { + g_free(client->interface); + client->interface = NULL; + } + + if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) + client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); + else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) + client->interface = g_strdup(AUDIO_SINK_INTERFACE); + + if (!manager_find_device(&bdaddr, NULL, FALSE)) { + if (!bacmp(&bdaddr, BDADDR_ANY)) + goto failed; + if (!manager_create_device(&bdaddr, create_cb, client)) + goto failed; + return; + } + + dev = manager_find_device(&bdaddr, client->interface, TRUE); + if (!dev) + dev = manager_find_device(&bdaddr, client->interface, FALSE); + + if (!dev) + goto failed; + + client->access_mode = req->access_mode; + + if (client->caps) { + g_slist_foreach(client->caps, (GFunc) g_free, NULL); + g_slist_free(client->caps); + } + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + client->caps = g_slist_append(client->caps, media_transport); + + memset(&sbc_cap, 0, sizeof(sbc_cap)); + + sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; + sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; + sbc_cap.frequency = req->sbc_capabilities.frequency; + sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; + sbc_cap.subbands = req->sbc_capabilities.subbands; + sbc_cap.block_length = req->sbc_capabilities.block_length ; + sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; + sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + client->caps = g_slist_append(client->caps, media_codec); + + start_config(dev, client); + + return; + +failed: + unix_ipc_error(client, BT_SETCONFIGURATION_RSP, err ? : EIO); } static void handle_streamstart_req(struct unix_client *client, struct bt_streamstart_req *req) { - /* FIXME : to be really implemented */ - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_streamstart_rsp *rsp = (void *) buf; - struct bt_datafd_ind *ind = (void *) buf; - - memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_STREAMSTART_RSP; - rsp->posix_errno = 0; - unix_ipc_sendmsg(client, &rsp->h); + if (!client->dev) + goto failed; - memset(buf, 0, sizeof(buf)); - ind->h.msg_type = BT_STREAMFD_IND; - unix_ipc_sendmsg(client, &ind->h); + start_resume(client->dev, client); - if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) - error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); + return; +failed: + unix_ipc_error(client, BT_STREAMSTART_REQ, EIO); } static void handle_streamstop_req(struct unix_client *client, struct bt_streamstop_req *req) { - /* FIXME : to be implemented */ - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_streamstop_rsp *rsp = (void *) buf; + if (!client->dev) + goto failed; - memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_STREAMSTOP_RSP; - rsp->posix_errno = 0; + start_suspend(client->dev, client); - unix_ipc_sendmsg(client, &rsp->h); + return; + +failed: + unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO); } static void handle_control_req(struct unix_client *client, @@ -565,8 +900,8 @@ static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) break; } - if (client->cancel_stream && client->req_id > 0) - client->cancel_stream(client->dev, client->req_id); + if (client->cancel && client->req_id > 0) + client->cancel(client->dev, client->req_id); goto failed; } -- cgit From d51c190ddd0c2774509d0284979b24b9b0f3524a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 4 Dec 2007 19:42:00 +0000 Subject: Fix error messages. (thanks to fchevalier for the patch) --- audio/ctl_bluetooth.c | 14 +++++++------- audio/gsta2dpsink.c | 30 ++++++++++++++++-------------- audio/ipc.h | 43 ++++++++++++++++++++++--------------------- audio/pcm_bluetooth.c | 46 ++++++++++++++++++++++++---------------------- audio/unix.c | 40 ++++++++++++++++++++-------------------- 5 files changed, 89 insertions(+), 84 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 5c198b1a..04185b36 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -164,24 +164,24 @@ static int bluetooth_send_ctl(struct bluetooth_data *data, return -errno; } - type = bt_audio_strmsg(ctl_rsp->h.msg_type); + type = bt_audio_strmsg(ctl_rsp->rsp_h.msg_h.msg_type); if (!type) { SNDERR("Bogus message type %d " "received from audio service", - ctl_rsp->h.msg_type); + ctl_rsp->rsp_h.msg_h.msg_type); return -EINVAL; } - if (ctl_rsp->h.msg_type != BT_CONTROL_RSP) { + if (ctl_rsp->rsp_h.msg_h.msg_type != BT_CONTROL_RSP) { SNDERR("Unexpected message %s received", type); return -EINVAL; } - if (ctl_rsp->posix_errno != 0) { + if (ctl_rsp->rsp_h.posix_errno != 0) { SNDERR("BT_CONTROL failed : %s (%d)", - strerror(ctl_rsp->posix_errno), - ctl_rsp->posix_errno); - return -ctl_rsp->posix_errno; + strerror(ctl_rsp->rsp_h.posix_errno), + ctl_rsp->rsp_h.posix_errno); + return -ctl_rsp->rsp_h.posix_errno; } return 0; diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 1b893cc1..5798bc24 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -582,6 +582,7 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) { gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_getcapabilities_req *req = (void *) buf; + bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; struct bt_getcapabilities_rsp *rsp = (void *) buf; GIOError io_error; @@ -595,17 +596,17 @@ static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) GST_ERROR_OBJECT(self, "Error while asking device caps"); } - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while getting device caps"); return FALSE; } - if (rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", - strerror(rsp->posix_errno), - rsp->posix_errno); + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); return FALSE; } @@ -660,8 +661,8 @@ static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) { gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_streamstart_req *req = (void *) buf; - struct bt_streamstart_rsp *rsp = (void *) buf; - struct bt_datafd_ind *ind = (void*) buf; + bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; + struct bt_streamfd_ind *ind = (void*) buf; GIOError io_error; GST_DEBUG_OBJECT(self, "stream start"); @@ -678,17 +679,17 @@ static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) GST_DEBUG_OBJECT(self, "stream start packet sent"); - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, BT_STREAMSTART_RSP); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while stream start confirmation"); return FALSE; } - if (rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", - strerror(rsp->posix_errno), - rsp->posix_errno); + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); return FALSE; } @@ -711,6 +712,7 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) { gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_setconfiguration_req *req = (void *) buf; + bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; struct bt_setconfiguration_rsp *rsp = (void *) buf; gboolean ret; GIOError io_error; @@ -737,17 +739,17 @@ static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) GST_DEBUG_OBJECT(self, "configuration packet sent"); - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp->h, + io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, BT_SETCONFIGURATION_RSP); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); return FALSE; } - if (rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", - strerror(rsp->posix_errno), - rsp->posix_errno); + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); return FALSE; } diff --git a/audio/ipc.h b/audio/ipc.h index 096c29f1..3e1200fa 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -74,11 +74,17 @@ extern "C" { #define BT_AUDIO_IPC_PACKET_SIZE 128 #define BT_IPC_SOCKET_NAME "\0/org/bluez/audio" -/* Generic message header definition */ +/* Generic message header definition, except for RSP messages */ typedef struct { uint8_t msg_type; } __attribute__ ((packed)) bt_audio_msg_header_t; +/* Generic message header definition, for all RSP messages */ +typedef struct { + bt_audio_msg_header_t msg_h; + uint8_t posix_errno; +} __attribute__ ((packed)) bt_audio_rsp_msg_header_t; + /* Messages list */ #define BT_GETCAPABILITIES_REQ 0 #define BT_GETCAPABILITIES_RSP 1 @@ -161,12 +167,11 @@ typedef struct { } __attribute__ ((packed)) mpeg_capabilities_t; struct bt_getcapabilities_rsp { - bt_audio_msg_header_t h; - uint8_t posix_errno; - uint8_t transport; /* Granted transport */ - sbc_capabilities_t sbc_capabilities; /* A2DP only */ - mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ - uint16_t sampling_rate; /* SCO only */ + bt_audio_rsp_msg_header_t rsp_h; + uint8_t transport; /* Granted transport */ + sbc_capabilities_t sbc_capabilities; /* A2DP only */ + mpeg_capabilities_t mpeg_capabilities; /* A2DP only */ + uint16_t sampling_rate; /* SCO only */ } __attribute__ ((packed)); /* BT_SETCONFIGURATION_REQ */ @@ -182,11 +187,10 @@ struct bt_setconfiguration_req { /* BT_SETCONFIGURATION_RSP */ struct bt_setconfiguration_rsp { - bt_audio_msg_header_t h; - uint8_t transport; /* Granted transport */ - uint8_t access_mode; /* Granted access mode */ - uint16_t link_mtu; /* Max length that transport supports */ - uint8_t posix_errno; + bt_audio_rsp_msg_header_t rsp_h; + uint8_t transport; /* Granted transport */ + uint8_t access_mode; /* Granted access mode */ + uint16_t link_mtu; /* Max length that transport supports */ } __attribute__ ((packed)); /* BT_STREAMSTART_REQ */ @@ -199,14 +203,13 @@ struct bt_streamstart_req { /* BT_STREAMSTART_RSP */ struct bt_streamstart_rsp { - bt_audio_msg_header_t h; - uint8_t posix_errno; + bt_audio_rsp_msg_header_t rsp_h; } __attribute__ ((packed)); /* BT_STREAMFD_IND */ /* This message is followed by one byte of data containing the stream data fd as ancilliary data */ -struct bt_datafd_ind { +struct bt_streamfd_ind { bt_audio_msg_header_t h; } __attribute__ ((packed)); @@ -217,8 +220,7 @@ struct bt_streamstop_req { /* BT_STREAMSTOP_RSP */ struct bt_streamstop_rsp { - bt_audio_msg_header_t h; - uint8_t posix_errno; + bt_audio_rsp_msg_header_t rsp_h; } __attribute__ ((packed)); /* BT_STREAMSUSPEND_IND */ @@ -255,10 +257,9 @@ struct bt_control_req { /* BT_CONTROL_RSP */ struct bt_control_rsp { - bt_audio_msg_header_t h; - uint8_t posix_errno; - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ + bt_audio_rsp_msg_header_t rsp_h; + uint8_t mode; /* Control Mode */ + uint8_t key; /* Control Key */ } __attribute__ ((packed)); /* BT_CONTROL_IND */ diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a177274a..6f67ca57 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -332,8 +332,8 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) char c = 'w'; char buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_streamstart_req *start_req = (void*) buf; - struct bt_streamstart_rsp *start_rsp = (void*) buf; - struct bt_datafd_ind *datafd_ind = (void*) buf; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; + struct bt_streamfd_ind *streamfd_ind = (void*) buf; uint32_t period_count = io->buffer_size / io->period_size; int opt_name, err; struct timeval t = { 0, period_count }; @@ -368,19 +368,19 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) if (err < 0) return err; - err = audioservice_expect(data->server.fd, &start_rsp->h, + err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, BT_STREAMSTART_RSP); if (err < 0) return err; - if (start_rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { SNDERR("BT_START failed : %s(%d)", - strerror(start_rsp->posix_errno), - start_rsp->posix_errno); - return -start_rsp->posix_errno; + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); + return -rsp_hdr->posix_errno; } - err = audioservice_expect(data->server.fd, &datafd_ind->h, + err = audioservice_expect(data->server.fd, &streamfd_ind->h, BT_STREAMFD_IND); if (err < 0) return err; @@ -542,6 +542,7 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, struct bluetooth_data *data = io->private_data; struct bluetooth_a2dp *a2dp = &data->a2dp; char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; struct bt_setconfiguration_req *setconf_req = (void*) buf; struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; unsigned int rate, channels; @@ -573,16 +574,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, if (err < 0) return err; - err = audioservice_expect(data->server.fd, &setconf_rsp->h, + err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, BT_SETCONFIGURATION_RSP); if (err < 0) return err; - if (setconf_rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { SNDERR("BT_SETCONFIGURATION failed : %s(%d)", - strerror(setconf_rsp->posix_errno), - setconf_rsp->posix_errno); - return -setconf_rsp->posix_errno; + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); + return -rsp_hdr->posix_errno; } data->transport = setconf_rsp->transport; @@ -1399,16 +1400,16 @@ static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) return err; } -static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, +static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) { - int err = audioservice_recv(sk, outmsg); + int err = audioservice_recv(sk, rsp_hdr); if (err == 0) { - if (outmsg->msg_type != expected_type) { + if (rsp_hdr->msg_type != expected_type) { err = -EINVAL; SNDERR("Bogus message %s received while " "%s was expected", - bt_audio_strmsg(outmsg->msg_type), + bt_audio_strmsg(rsp_hdr->msg_type), bt_audio_strmsg(expected_type)); } } @@ -1421,6 +1422,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, int sk, err; struct bluetooth_alsa_config *alsa_conf = &data->alsa_config; char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; struct bt_getcapabilities_req *getcaps_req = (void*) buf; struct bt_getcapabilities_rsp *getcaps_rsp = (void*) buf; @@ -1474,15 +1476,15 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, if (err < 0) goto failed; - err = audioservice_expect(data->server.fd, &getcaps_rsp->h, BT_GETCAPABILITIES_RSP); + err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, BT_GETCAPABILITIES_RSP); if (err < 0) goto failed; - if (getcaps_rsp->posix_errno != 0) { + if (rsp_hdr->posix_errno != 0) { SNDERR("BT_GETCAPABILITIES failed : %s(%d)", - strerror(getcaps_rsp->posix_errno), - getcaps_rsp->posix_errno); - return -getcaps_rsp->posix_errno; + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); + return -rsp_hdr->posix_errno; } data->transport = getcaps_rsp->transport; diff --git a/audio/unix.c b/audio/unix.c index c321b99b..8267e895 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -161,13 +161,13 @@ static void unix_ipc_sendmsg(struct unix_client *client, static void unix_ipc_error(struct unix_client *client, int type, int err) { char buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_getcapabilities_rsp *rsp = (void *) buf; + bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = type; - rsp->posix_errno = err; + rsp_hdr->msg_h.msg_type = type; + rsp_hdr->posix_errno = err; - unix_ipc_sendmsg(client, &rsp->h); + unix_ipc_sendmsg(client, &rsp_hdr->msg_h); } static service_type_t select_service(struct device *dev, const char *interface) @@ -304,13 +304,13 @@ static void headset_setup_complete(struct device *dev, void *user_data) memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_SETCONFIGURATION_RSP; + rsp->rsp_h.msg_h.msg_type = BT_SETCONFIGURATION_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; rsp->access_mode = client->access_mode; client->data_fd = headset_get_sco_fd(dev); - unix_ipc_sendmsg(client, &rsp->h); + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); } static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, @@ -330,7 +330,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, memset(buf, 0, sizeof(buf)); client->req_id = 0; - rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->rsp_h.msg_h.msg_type = BT_GETCAPABILITIES_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; for (l = seps; l; l = g_slist_next(l)) { @@ -361,7 +361,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; } - unix_ipc_sendmsg(client, &rsp->h); + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); return; @@ -406,14 +406,14 @@ static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, goto failed; } - rsp->h.msg_type = BT_SETCONFIGURATION_RSP; + rsp->rsp_h.msg_h.msg_type = BT_SETCONFIGURATION_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_A2DP; client->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; rsp->access_mode = client->access_mode; /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ rsp->link_mtu = omtu; - unix_ipc_sendmsg(client, &rsp->h); + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); client->cb_id = avdtp_stream_add_cb(session, stream, stream_state_changed, client); @@ -441,13 +441,13 @@ static void a2dp_resume_complete(struct avdtp *session, struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_streamstart_rsp *rsp = (void *) buf; - struct bt_datafd_ind *ind = (void *) buf; + struct bt_streamfd_ind *ind = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_STREAMSTART_RSP; - rsp->posix_errno = 0; - unix_ipc_sendmsg(client, &rsp->h); + rsp->rsp_h.msg_h.msg_type = BT_STREAMSTART_RSP; + rsp->rsp_h.posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); memset(buf, 0, sizeof(buf)); ind->h.msg_type = BT_STREAMFD_IND; @@ -487,9 +487,9 @@ static void a2dp_suspend_complete(struct avdtp *session, goto failed; memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_STREAMSTOP_RSP; - rsp->posix_errno = 0; - unix_ipc_sendmsg(client, &rsp->h); + rsp->rsp_h.msg_h.msg_type = BT_STREAMSTOP_RSP; + rsp->rsp_h.posix_errno = 0; + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); return; @@ -863,10 +863,10 @@ static void handle_control_req(struct unix_client *client, struct bt_setconfiguration_rsp *rsp = (void *) buf; memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_CONTROL_RSP; - rsp->posix_errno = 0; + rsp->rsp_h.msg_h.msg_type = BT_CONTROL_RSP; + rsp->rsp_h.posix_errno = 0; - unix_ipc_sendmsg(client, &rsp->h); + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); } static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) -- cgit From 2824d253c5b10f19ae37dca1c36705ee4edf84c0 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 5 Dec 2007 18:56:30 +0000 Subject: Cache discovery process to avoid delays. --- audio/avdtp.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 3acd4128..cb2a99e6 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1825,20 +1825,14 @@ static gboolean avdtp_discover_resp(struct avdtp *session, resp->seps[i].media_type, resp->seps[i].inuse); /* Skip SEP's which are in use */ -/* if (resp->seps[i].inuse) continue; -*/ sep = find_remote_sep(session->seps, resp->seps[i].seid); if (!sep) { sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); } -/* - else if (sep && sep->stream) - continue; -*/ sep->seid = resp->seps[i].seid; sep->type = resp->seps[i].type; @@ -2369,6 +2363,11 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_dat if (session->discov_cb) return -EBUSY; + if (session->seps) { + cb(session, session->seps, NULL, user_data); + return 0; + } + memset(&req, 0, sizeof(req)); init_request(&req.header, AVDTP_DISCOVER); -- cgit From 721d249e314b0591274436594be56805bf5a2822 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 6 Dec 2007 14:42:41 +0000 Subject: Fixes for hsp. --- audio/pcm_bluetooth.c | 59 ++++++++++++++++++++---- audio/unix.c | 123 +++++++++++++++++++++++++++++--------------------- 2 files changed, 121 insertions(+), 61 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6f67ca57..b0e3838b 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -422,6 +422,49 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) return write(data->pipefd[1], &c, 1); } +static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, + snd_pcm_hw_params_t *params) +{ + struct bluetooth_data *data = io->private_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; + struct bt_setconfiguration_req *setconf_req = (void*) buf; + struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; + int err; + + DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", + io->period_size, io->buffer_size); + + memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; + strncpy(setconf_req->device, data->alsa_config.device, 18); + setconf_req->transport = BT_CAPABILITIES_TRANSPORT_SCO; + setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? + BT_CAPABILITIES_ACCESS_MODE_WRITE : + BT_CAPABILITIES_ACCESS_MODE_READ); + + err = audioservice_send(data->server.fd, &setconf_req->h); + if (err < 0) + return err; + + err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, + BT_SETCONFIGURATION_RSP); + if (err < 0) + return err; + + if (rsp_hdr->posix_errno != 0) { + SNDERR("BT_SETCONFIGURATION failed : %s(%d)", + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); + return -rsp_hdr->posix_errno; + } + + data->transport = setconf_rsp->transport; + data->link_mtu = setconf_rsp->link_mtu; + + return 0; +} + static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { switch (freq) { @@ -565,6 +608,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; + strncpy(setconf_req->device, data->alsa_config.device, 18); + setconf_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; setconf_req->sbc_capabilities = active_capabilities; setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? BT_CAPABILITIES_ACCESS_MODE_WRITE : @@ -762,7 +807,7 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, /* Increment hardware transmition pointer */ data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) % - io->buffer_size; + io->buffer_size; proceed: buff = (unsigned char *) areas->addr + @@ -1020,6 +1065,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .stop = bluetooth_playback_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, .poll_descriptors = bluetooth_playback_poll_descriptors, @@ -1032,6 +1078,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { .stop = bluetooth_stop, .pointer = bluetooth_pointer, .close = bluetooth_close, + .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_read, .poll_descriptors = bluetooth_poll_descriptors, @@ -1467,11 +1514,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, getcaps_req->transport = alsa_conf->transport; else getcaps_req->transport = BT_CAPABILITIES_TRANSPORT_ANY; -/* - getcaps_req->access_mode = (stream == SND_PCM_STREAM_PLAYBACK ? - BT_CAPABILITIES_ACCESS_MODE_WRITE : - BT_CAPABILITIES_ACCESS_MODE_READ); -*/ + err = audioservice_send(data->server.fd, &getcaps_req->h); if (err < 0) goto failed; @@ -1488,9 +1531,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, } data->transport = getcaps_rsp->transport; -/* - data->link_mtu = getcaps_rsp->link_mtu; -*/ + if (getcaps_rsp->transport == BT_CAPABILITIES_TRANSPORT_A2DP) data->a2dp.sbc_capabilities = getcaps_rsp->sbc_capabilities; diff --git a/audio/unix.c b/audio/unix.c index 8267e895..442dca79 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -216,55 +216,34 @@ static void stream_state_changed(struct avdtp_stream *stream, break; } } -/* + static void headset_discovery_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; struct bt_getcapabilities_rsp *rsp = (void *) buf; - struct headset_data *hs = &client->d.hs; client->req_id = 0; - if (!dev) { - unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); - client->dev = NULL; - return; - } - - switch (client->access_mode) { - case BT_CAPABILITIES_ACCESS_MODE_READ: - hs->lock = HEADSET_LOCK_READ; - break; - case BT_CAPABILITIES_ACCESS_MODE_WRITE: - hs->lock = HEADSET_LOCK_WRITE; - break; - case BT_CAPABILITIES_ACCESS_MODE_READWRITE: - hs->lock = HEADSET_LOCK_READ | HEADSET_LOCK_WRITE; - break; - default: - hs->lock = 0; - break; - } - - if (!headset_lock(dev, hs->lock)) { - error("Unable to lock headset"); - unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); - client->dev = NULL; - return; - } + if (!dev) + goto failed; memset(buf, 0, sizeof(buf)); - rsp->h.msg_type = BT_GETCAPABILITIES_RSP; + rsp->rsp_h.msg_h.msg_type = BT_GETCAPABILITIES_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; rsp->sampling_rate = 8000; - client->data_fd = headset_get_sco_fd(dev); + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); - unix_ipc_sendmsg(client, &rsp->h); + return; + +failed: + error("discovery failed"); + unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); + client->dev = NULL; } -*/ + static void headset_setup_complete(struct device *dev, void *user_data) { struct unix_client *client = user_data; @@ -274,11 +253,8 @@ static void headset_setup_complete(struct device *dev, void *user_data) client->req_id = 0; - if (!dev) { - unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); - client->dev = NULL; - return; - } + if (!dev) + goto failed; switch (client->access_mode) { case BT_CAPABILITIES_ACCESS_MODE_READ: @@ -297,9 +273,7 @@ static void headset_setup_complete(struct device *dev, void *user_data) if (!headset_lock(dev, hs->lock)) { error("Unable to lock headset"); - unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); - client->dev = NULL; - return; + goto failed; } memset(buf, 0, sizeof(buf)); @@ -307,10 +281,55 @@ static void headset_setup_complete(struct device *dev, void *user_data) rsp->rsp_h.msg_h.msg_type = BT_SETCONFIGURATION_RSP; rsp->transport = BT_CAPABILITIES_TRANSPORT_SCO; rsp->access_mode = client->access_mode; + rsp->link_mtu = 48; client->data_fd = headset_get_sco_fd(dev); unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); + + return; + +failed: + error("config failed"); + unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); + client->dev = NULL; +} + +static void headset_resume_complete(struct device *dev, void *user_data) +{ + struct unix_client *client = user_data; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_streamfd_ind *ind = (void *) buf; + + client->req_id = 0; + + if (!dev) + goto failed; + + memset(buf, 0, sizeof(buf)); + + rsp->rsp_h.msg_h.msg_type = BT_STREAMSTART_RSP; + + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); + + memset(buf, 0, sizeof(buf)); + ind->h.msg_type = BT_STREAMFD_IND; + unix_ipc_sendmsg(client, &ind->h); + + client->data_fd = headset_get_sco_fd(dev); + + if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { + error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); + goto failed; + } + + return; + +failed: + error("resume failed"); + unix_ipc_error(client, BT_STREAMSTART_RSP, EIO); + client->dev = NULL; } static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, @@ -421,7 +440,7 @@ static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, return; failed: - error("setup failed"); + error("config failed"); if (a2dp->sep) { a2dp_sep_unlock(a2dp->sep, a2dp->session); @@ -511,7 +530,6 @@ failed: static void start_discovery(struct device *dev, struct unix_client *client) { struct a2dp_data *a2dp; - unsigned int id; int err = 0; client->type = select_service(dev, client->interface); @@ -535,8 +553,7 @@ static void start_discovery(struct device *dev, struct unix_client *client) break; case TYPE_HEADSET: - id = headset_request_stream(dev, headset_setup_complete, client); - client->cancel = headset_cancel_stream; + headset_discovery_complete(dev, client); break; default: @@ -544,6 +561,8 @@ static void start_discovery(struct device *dev, struct unix_client *client) goto failed; } + client->dev = dev; + return; failed: @@ -625,11 +644,16 @@ static void start_resume(struct device *dev, struct unix_client *client) id = a2dp_source_resume(a2dp->session, a2dp->sep, a2dp_resume_complete, client); client->cancel = a2dp_source_cancel; + + if (id == 0) { + error("resume failed"); + goto failed; + } + break; case TYPE_HEADSET: - id = headset_request_stream(dev, headset_setup_complete, client); - client->cancel = headset_cancel_stream; + headset_resume_complete(dev, client); break; default: @@ -637,11 +661,6 @@ static void start_resume(struct device *dev, struct unix_client *client) goto failed; } - if (id == 0) { - error("resume failed"); - goto failed; - } - return; failed: -- cgit From 1ddd76afa25d775788e3762ab731cad80f8ffe56 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 06:18:59 +0000 Subject: Minor coding style fixes --- audio/a2dp.c | 2 +- audio/avdtp.c | 14 +++++++++----- audio/headset.c | 2 +- audio/unix.c | 16 +++++++++------- 4 files changed, 20 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index bf644359..95040873 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -270,7 +270,7 @@ static void stream_state_changed(struct avdtp_stream *stream, avdtp_unref(sep->session); sep->session = NULL; } - + sep->stream = NULL; } diff --git a/audio/avdtp.c b/audio/avdtp.c index cb2a99e6..b0b5b7cd 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2079,7 +2079,8 @@ static gboolean conf_rej_to_err(struct conf_rej *rej, int size, } static gboolean stream_rej_to_err(struct stream_rej *rej, int size, - struct avdtp_error *err, uint8_t *acp_seid) + struct avdtp_error *err, + uint8_t *acp_seid) { if (size < sizeof(struct conf_rej)) { error("Too small packet for stream_rej"); @@ -2094,8 +2095,9 @@ static gboolean stream_rej_to_err(struct stream_rej *rej, int size, return TRUE; } -static gboolean avdtp_parse_rej(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_header *header, int size) +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_header *header, int size) { struct avdtp_error err; uint8_t acp_seid, category; @@ -2355,7 +2357,8 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, return cap; } -int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data) +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data) { struct gen_req req; int ret; @@ -2879,7 +2882,8 @@ static GIOChannel *avdtp_server_socket(void) lm = L2CAP_LM_SECURE; if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { - error("AVDTP server setsockopt: %s (%d)", strerror(errno), errno); + error("AVDTP server setsockopt: %s (%d)", strerror(errno), + errno); close(sock); return NULL; } diff --git a/audio/headset.c b/audio/headset.c index 6cb5406d..97419a64 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -65,7 +65,7 @@ #define HEADSET_GAIN_MICROPHONE 'M' static char *str_state[] = { - "HEADSET_STATE_DISCONNECTED", + "HEADSET_STATE_DISCONNECTED", "HEADSET_STATE_CONNECT_IN_PROGRESS", "HEADSET_STATE_CONNECTED", "HEADSET_STATE_PLAY_IN_PROGRESS", diff --git a/audio/unix.c b/audio/unix.c index 442dca79..d3e7485c 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -122,10 +122,10 @@ static void client_free(struct unix_client *client) g_free(client); } -/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly AF_UNIX) -and the sendmsg() system call with the cmsg_type field of a "struct cmsghdr" set -to SCM_RIGHTS and the data being an integer value equal to the handle of the -file descriptor to be passed.*/ +/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly + * AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct + * cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the + * handle of the file descriptor to be passed. */ static int unix_sendmsg_fd(int sock, int fd) { char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm'; @@ -397,7 +397,7 @@ failed: static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) + void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -420,7 +420,8 @@ static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, a2dp->sep = sep; a2dp->stream = stream; - if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, &caps)) { + if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, + &caps)) { error("Unable to get stream transport"); goto failed; } @@ -1027,7 +1028,8 @@ int unix_init(void) } if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("Can't bind unix socket: %s (%d)", strerror(errno), errno); + error("Can't bind unix socket: %s (%d)", strerror(errno), + errno); close(sk); return -1; } -- cgit From c7514bc9b27c43fadfcb02180e7d8875890c7820 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 08:03:02 +0000 Subject: Fix BT_GETCAPABILITIES when the headset has already been acting as INT --- audio/avdtp.c | 8 ++++---- audio/unix.c | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index b0b5b7cd..98cce35f 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1824,12 +1824,12 @@ static gboolean avdtp_discover_resp(struct avdtp *session, resp->seps[i].seid, resp->seps[i].type, resp->seps[i].media_type, resp->seps[i].inuse); - /* Skip SEP's which are in use */ - if (resp->seps[i].inuse) - continue; - sep = find_remote_sep(session->seps, resp->seps[i].seid); if (!sep) { + if (resp->seps[i].inuse && + !find_stream_by_rseid(session, + resp->seps[i].seid)) + continue; sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); } diff --git a/audio/unix.c b/audio/unix.c index d3e7485c..31e1dcb2 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -364,9 +364,10 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, codec_cap = (void *) cap->data; - if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) + if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) { sbc_cap = (void *) codec_cap; - + break; + } } /* endianess prevent direct cast */ -- cgit From bcc1d569bcfb0cd026930eb6a8ee607a8002f523 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 08:04:04 +0000 Subject: Fix unitialized memory access issue --- audio/a2dp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 95040873..7b17f941 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1016,7 +1016,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, debug("a2dp_source_config: selected SEP %p", sep); - cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->config_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; @@ -1137,7 +1137,7 @@ unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; - cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->suspend_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; -- cgit From 29d3944a4099800ed3c27e3389acc5c7eb326805 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 08:14:01 +0000 Subject: Make sure remote SEP cache and active streams stay in sync --- audio/avdtp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 98cce35f..955576b7 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1812,6 +1812,7 @@ static gboolean avdtp_discover_resp(struct avdtp *session, struct discover_resp *resp, int size) { int sep_count, i, isize = sizeof(struct seid_info); + struct avdtp_stream *stream; sep_count = (size - sizeof(struct avdtp_header)) / isize; @@ -1824,16 +1825,17 @@ static gboolean avdtp_discover_resp(struct avdtp *session, resp->seps[i].seid, resp->seps[i].type, resp->seps[i].media_type, resp->seps[i].inuse); + stream = find_stream_by_rseid(session, resp->seps[i].seid); + sep = find_remote_sep(session->seps, resp->seps[i].seid); if (!sep) { - if (resp->seps[i].inuse && - !find_stream_by_rseid(session, - resp->seps[i].seid)) + if (resp->seps[i].inuse && !stream) continue; sep = g_new0(struct avdtp_remote_sep, 1); session->seps = g_slist_append(session->seps, sep); } + sep->stream = stream; sep->seid = resp->seps[i].seid; sep->type = resp->seps[i].type; sep->media_type = resp->seps[i].media_type; -- cgit From 1b2f232ee7f343178d90dc194a773cf45f26a84b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 08:15:57 +0000 Subject: Minor locality issue --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 955576b7..d5433452 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1812,12 +1812,12 @@ static gboolean avdtp_discover_resp(struct avdtp *session, struct discover_resp *resp, int size) { int sep_count, i, isize = sizeof(struct seid_info); - struct avdtp_stream *stream; sep_count = (size - sizeof(struct avdtp_header)) / isize; for (i = 0; i < sep_count; i++) { struct avdtp_remote_sep *sep; + struct avdtp_stream *stream; struct seid_req req; int ret; -- cgit From 70f4ad8b4bf7d477bcfc7efdec7f25b1f9517cd3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 08:52:31 +0000 Subject: Fix another potential uninitialized memory access issue --- audio/a2dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 7b17f941..e4f65f3e 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1082,7 +1082,7 @@ unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; - cb_data = g_new(struct a2dp_setup_cb, 1); + cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->resume_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; -- cgit From c020b7240f8f6b72ffbb6c6f327a63f2da14e6a9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 09:12:00 +0000 Subject: a2dp_source_suspend should call avdtp_suspend and not avdtp_start if the state is STREAMING --- audio/a2dp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index e4f65f3e..f0ada567 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1163,8 +1163,8 @@ unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, g_idle_add((GSourceFunc) finalize_suspend, setup); break; case AVDTP_STATE_STREAMING: - if (avdtp_start(session, sep->stream) < 0) { - error("avdtp_start failed"); + if (avdtp_suspend(session, sep->stream) < 0) { + error("avdtp_suspend failed"); goto failed; } break; -- cgit From 3f4eef5217c126cf27afb32c5fbb10cb08726658 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 7 Dec 2007 09:36:32 +0000 Subject: Bring back start variable to fix racecondition with quickly reconnecting IPC clients --- audio/a2dp.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index f0ada567..b364cf8d 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -82,6 +82,7 @@ struct a2dp_setup { GSList *client_caps; gboolean reconfigure; gboolean canceled; + gboolean start; GSList *cb; int ref; }; @@ -586,6 +587,7 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; + gboolean start; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Suspend_Cfm"); @@ -598,12 +600,27 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (!setup) return; + start = setup->start; + setup->start = FALSE; + if (err) { setup->stream = NULL; finalize_suspend(setup, err); } else finalize_suspend_errno(setup, 0); + + if (!start) + return; + + if (err) + finalize_resume(setup, err); + else if (avdtp_start(session, a2dp_sep->stream) < 0) { + struct avdtp_error start_err; + error("avdtp_start failed"); + avdtp_error_init(&start_err, AVDTP_ERROR_ERRNO, EIO); + finalize_resume(setup, &start_err); + } } static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -1116,7 +1133,10 @@ unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, avdtp_unref(sep->session); sep->session = NULL; } - g_idle_add((GSourceFunc) finalize_resume, setup); + if (sep->suspending) + setup->start = TRUE; + else + g_idle_add((GSourceFunc) finalize_resume, setup); break; default: error("SEP in bad state"); -- cgit From 3b8deddf131599756e7fd3470b18af14e8bcc971 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 7 Dec 2007 21:24:26 +0000 Subject: Fix bug of error responses being ignored. --- audio/unix.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 31e1dcb2..d9f7c670 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -407,6 +407,9 @@ static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, uint16_t imtu, omtu; GSList *caps; + if (err) + goto failed; + memset(buf, 0, sizeof(buf)); client->req_id = 0; @@ -465,6 +468,9 @@ static void a2dp_resume_complete(struct avdtp *session, struct bt_streamfd_ind *ind = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; + if (err) + goto failed; + memset(buf, 0, sizeof(buf)); rsp->rsp_h.msg_h.msg_type = BT_STREAMSTART_RSP; rsp->rsp_h.posix_errno = 0; @@ -488,7 +494,7 @@ failed: a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } - unix_ipc_error(client, BT_STREAMSTART_REQ, EIO); + unix_ipc_error(client, BT_STREAMSTART_RSP, EIO); avdtp_unref(a2dp->session); @@ -521,7 +527,7 @@ failed: a2dp_sep_unlock(a2dp->sep, a2dp->session); a2dp->sep = NULL; } - unix_ipc_error(client, BT_STREAMSTOP_REQ, EIO); + unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO); avdtp_unref(a2dp->session); -- cgit From 5309e42d65942fb971c0748676939398e6e513f4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 7 Dec 2007 22:29:26 +0000 Subject: Fixes use of g_idle_add function. --- audio/a2dp.c | 57 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 22 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index b364cf8d..74699cd5 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -79,6 +79,7 @@ struct a2dp_setup { struct avdtp *session; struct a2dp_sep *sep; struct avdtp_stream *stream; + struct avdtp_error *err; GSList *client_caps; gboolean reconfigure; gboolean canceled; @@ -137,7 +138,7 @@ static struct device *a2dp_get_dev(struct avdtp *session) return manager_device_connected(&addr, A2DP_SOURCE_UUID); } -static gboolean finalize_config(struct a2dp_setup *s, struct avdtp_error *err) +static gboolean finalize_config(struct a2dp_setup *s) { GSList *l; @@ -146,7 +147,7 @@ static gboolean finalize_config(struct a2dp_setup *s, struct avdtp_error *err) struct a2dp_setup_cb *cb = l->data; if (cb->config_cb) { - cb->config_cb(s->session, s->sep, s->stream, err, + cb->config_cb(s->session, s->sep, s->stream, s->err, cb->user_data); cb->config_cb = NULL; setup_unref(s); @@ -162,11 +163,12 @@ static gboolean finalize_config_errno(struct a2dp_setup *s, int err) struct avdtp_error avdtp_err; avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + s->err = err ? &avdtp_err : NULL; - return finalize_config(s, err ? &avdtp_err : NULL); + return finalize_config(s); } -static gboolean finalize_resume(struct a2dp_setup *s, struct avdtp_error *err) +static gboolean finalize_resume(struct a2dp_setup *s) { GSList *l; @@ -175,7 +177,7 @@ static gboolean finalize_resume(struct a2dp_setup *s, struct avdtp_error *err) struct a2dp_setup_cb *cb = l->data; if (cb->resume_cb) { - cb->resume_cb(s->session, err, cb->user_data); + cb->resume_cb(s->session, s->err, cb->user_data); cb->resume_cb = NULL; setup_unref(s); } @@ -190,11 +192,12 @@ static gboolean finalize_resume_errno(struct a2dp_setup *s, int err) struct avdtp_error avdtp_err; avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + s->err = err ? &avdtp_err : NULL; - return finalize_resume(s, err ? &avdtp_err : NULL); + return finalize_resume(s); } -static gboolean finalize_suspend(struct a2dp_setup *s, struct avdtp_error *err) +static gboolean finalize_suspend(struct a2dp_setup *s) { GSList *l; @@ -203,7 +206,7 @@ static gboolean finalize_suspend(struct a2dp_setup *s, struct avdtp_error *err) struct a2dp_setup_cb *cb = l->data; if (cb->suspend_cb) { - cb->suspend_cb(s->session, err, cb->user_data); + cb->suspend_cb(s->session, s->err, cb->user_data); cb->suspend_cb = NULL; setup_unref(s); } @@ -218,8 +221,9 @@ static gboolean finalize_suspend_errno(struct a2dp_setup *s, int err) struct avdtp_error avdtp_err; avdtp_error_init(&avdtp_err, AVDTP_ERROR_ERRNO, -err); + s->err = err ? &avdtp_err : NULL; - return finalize_suspend(s, err ? &avdtp_err : NULL); + return finalize_suspend(s); } static struct a2dp_setup *find_setup_by_session(struct avdtp *session) @@ -399,8 +403,10 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, setup = find_setup_by_session(session); if (err) { - if (setup) - finalize_config(setup, err); + if (setup) { + setup->err = err; + finalize_config(setup); + } return; } @@ -489,10 +495,11 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - finalize_config(setup, err); + setup->err = err; + finalize_config(setup); } else - finalize_config(setup, 0); + finalize_config_errno(setup, 0); } static gboolean suspend_timeout(struct a2dp_sep *sep) @@ -554,7 +561,8 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - finalize_resume(setup, err); + setup->err = err; + finalize_resume(setup); } else finalize_resume_errno(setup, 0); @@ -605,7 +613,8 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - finalize_suspend(setup, err); + setup->err = err; + finalize_suspend(setup); } else finalize_suspend_errno(setup, 0); @@ -613,13 +622,15 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (!start) return; - if (err) - finalize_resume(setup, err); - else if (avdtp_start(session, a2dp_sep->stream) < 0) { + if (err) { + setup->err = err; + finalize_suspend(setup); + } else if (avdtp_start(session, a2dp_sep->stream) < 0) { struct avdtp_error start_err; error("avdtp_start failed"); avdtp_error_init(&start_err, AVDTP_ERROR_ERRNO, EIO); - finalize_resume(setup, &start_err); + setup->err = err; + finalize_suspend(setup); } } @@ -687,7 +698,8 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - finalize_config(setup, err); + setup->err = err; + finalize_config(setup); return; } @@ -767,10 +779,11 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, if (err) { setup->stream = NULL; - finalize_config(setup, err); + setup->err = err; + finalize_config(setup); } else - finalize_config(setup, 0); + finalize_config_errno(setup, 0); } static struct avdtp_sep_cfm cfm = { -- cgit From d3a05810c6855dbd8664b29a984726d80f42d3dd Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 11 Dec 2007 14:39:30 +0000 Subject: Fix connecting to hfp headset. (Patch from fdalleau) --- audio/headset.c | 37 ++++++++++++++++++++++++++++++------- audio/headset.h | 4 ++-- audio/manager.c | 9 +++++++-- 3 files changed, 39 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 97419a64..7bb1746d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -99,6 +99,7 @@ struct headset { int data_start; int data_length; + int enable_hfp; headset_type_t type; headset_state_t state; @@ -111,6 +112,7 @@ struct headset { }; static int rfcomm_connect(struct device *device, struct pending_connect *c); +static int get_handles(struct device *device, struct pending_connect *c); static void pending_connect_free(struct pending_connect *c) { @@ -640,11 +642,21 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if ((uuid.type == SDP_UUID32 && + if (hs->type == SVC_HEADSET && + ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != HEADSET_SVCLASS_ID) || (uuid.type == SDP_UUID16 && - uuid.value.uuid16 != HEADSET_SVCLASS_ID)) { - error("Service classes did not contain the expected UUID"); + uuid.value.uuid16 != HEADSET_SVCLASS_ID))) { + error("Service classes did not contain the expected UUID hsp"); + goto failed_not_supported; + } + + if (hs->type == SVC_HANDSFREE && + ((uuid.type == SDP_UUID32 && + uuid.value.uuid32 != HANDSFREE_SVCLASS_ID) || + (uuid.type == SDP_UUID16 && + uuid.value.uuid16 != HANDSFREE_SVCLASS_ID))) { + error("Service classes did not contain the expected UUID hfp"); goto failed_not_supported; } @@ -750,7 +762,17 @@ static void get_handles_reply(DBusPendingCall *call, void *data) } if (array_len < 1) { - debug("No record handles found"); + + if(hs->type == SVC_HANDSFREE) { + debug("No record handles found for hfp"); + hs->type = SVC_HEADSET; + get_handles(device, c); + dbus_message_unref(reply); + return; + } + + debug("No record handles found for hsp"); + if (c->msg) error_not_supported(device->conn, c->msg); goto failed; @@ -862,7 +884,7 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) if (!g_slist_find(hs->pending, c)) hs->pending = g_slist_append(hs->pending, c); - hs->type = hs->hfp_handle ? SVC_HANDSFREE : SVC_HEADSET; + hs->type = hs->enable_hfp ? SVC_HANDSFREE : SVC_HEADSET; if (hs->state == HEADSET_STATE_DISCONNECTED) return get_handles(device, c); @@ -1368,8 +1390,8 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) headset_set_channel(headset, record); } -struct headset *headset_init(struct device *dev, sdp_record_t *record, - uint16_t svc) +struct headset *headset_init(struct device *dev, int enable_hfp, + sdp_record_t *record, uint16_t svc) { struct headset *hs; @@ -1377,6 +1399,7 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record, hs->rfcomm_ch = -1; hs->sp_gain = -1; hs->mic_gain = -1; + hs->enable_hfp = enable_hfp; if (!record) goto register_iface; diff --git a/audio/headset.h b/audio/headset.h index 5e19cd9a..b151dbd9 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -54,8 +54,8 @@ typedef enum { typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data); -struct headset *headset_init(struct device *dev, sdp_record_t *record, - uint16_t svc); +struct headset *headset_init(struct device *dev, int enable_hfp, + sdp_record_t *record, uint16_t svc); void headset_free(struct device *dev); diff --git a/audio/manager.c b/audio/manager.c index 507f60ac..cfadec47 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -108,6 +108,7 @@ static GIOChannel *hs_server = NULL; static GIOChannel *hf_server = NULL; static const struct enabled_interfaces *enabled; +static gboolean enable_hfp = FALSE; static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, @@ -254,6 +255,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, + enable_hfp, record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: @@ -265,6 +267,7 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, + enable_hfp, record, uuid16); break; case HANDSFREE_AGW_SVCLASS_ID: @@ -645,7 +648,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) if (device->headset) return device; - device->headset = headset_init(device, NULL, 0); + device->headset = headset_init(device, enable_hfp, NULL, 0); if (!device->headset) return NULL; @@ -1070,7 +1073,7 @@ static void parse_stored_devices(char *key, char *value, void *data) bacpy(&device->store, src); if (enabled->headset && strstr(value, "headset")) - device->headset = headset_init(device, NULL, 0); + device->headset = headset_init(device, enable_hfp, NULL, 0); if (enabled->sink && strstr(value, "sink")) device->sink = sink_init(device); if (enabled->control && strstr(value, "control")) @@ -1561,6 +1564,8 @@ static int headset_server_init(DBusConnection *conn, gboolean no_hfp) if (no_hfp) return 0; + enable_hfp = TRUE; + chan = DEFAULT_HF_AG_CHANNEL; hf_server = server_socket(&chan); -- cgit From e83ac56743aa266dbb05b7f8102ae0c73bd1bddf Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 12 Dec 2007 21:05:31 +0000 Subject: Add initial mechanism to handle headset commands. --- audio/headset.c | 269 +++++++++++++++++++++++++++++++------------------------- audio/headset.h | 8 +- 2 files changed, 149 insertions(+), 128 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 7bb1746d..e7425d5a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -108,92 +108,188 @@ struct headset { int sp_gain; int mic_gain; + unsigned int hfp_features; headset_lock_t lock; }; +struct event { + const char *cmd; + int (*callback) (struct device *device, const char *buf); +}; + static int rfcomm_connect(struct device *device, struct pending_connect *c); static int get_handles(struct device *device, struct pending_connect *c); -static void pending_connect_free(struct pending_connect *c) +static int headset_send(struct headset *hs, const char *str) { - if (c->io) { - g_io_channel_close(c->io); - g_io_channel_unref(c->io); + GIOError err; + gsize total_written, written, count; + + if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { + error("headset_send: the headset is not connected"); + return -EIO; } - if (c->msg) - dbus_message_unref(c->msg); - if (c->call) { - dbus_pending_call_cancel(c->call); - dbus_pending_call_unref(c->call); + + count = strlen(str); + written = total_written = 0; + + while (total_written < count) { + err = g_io_channel_write(hs->rfcomm, str + total_written, + count - total_written, &written); + if (err != G_IO_ERROR_NONE) + return -errno; + total_written += written; } - g_free(c); + return 0; +} + +static int supported_features(struct device *device, const char *buf) +{ + struct headset *hs = device->headset; + int err; + + hs->hfp_features = strtoul(&buf[8], NULL, 10); + + err = headset_send(hs, "\r\n+BRSF:0\r\n"); + if (err < 0) + return err; + + return headset_send(device->headset, "\r\nOK\r\n"); +} + +static int report_indicators(struct device *device, const char *buf) +{ + struct headset *hs = device->headset; + int err; + + if (buf[7] == '=') + err = headset_send(hs, "\r\n+CIND:(\"service\",(0,1))," + "(\"call\",(0,1)),(\"callsetup\",(0-3))\r\n"); + else + err = headset_send(hs, "\r\n+CIND:1, 0, 0\r\n"); + + if (err < 0) + return err; + + return headset_send(device->headset, "\r\nOK\r\n"); +} + +static int event_reporting(struct device *device, const char *buf) +{ + return headset_send(device->headset, "\r\nOK\r\n"); +} + +static int call_hold(struct device *device, const char *buf) +{ + struct headset *hs = device->headset; + int err; + + err = headset_send(hs, "\r\n+CHLD:(0,1,1x,2,2x,3,4)\r\n"); + if (err < 0) + return err; + + return headset_send(device->headset, "\r\nOK\r\n"); } -static void hs_signal_gain_setting(struct device *device, const char *buf) +static int answer_call(struct device *device, const char *buf) +{ + dbus_connection_emit_signal(device->conn, device->path, + AUDIO_HEADSET_INTERFACE, "AnswerRequested", + DBUS_TYPE_INVALID); + + return headset_send(device->headset, "\r\nOK\r\n"); +} + +static int terminate_call(struct device *device, const char *buf) +{ + return headset_send(device->headset, "\r\nOK\r\n"); +} + +static int signal_gain_setting(struct device *device, const char *buf) { const char *name; dbus_uint16_t gain; - if (strlen(buf) < 6) { + if (strlen(buf) < 8) { error("Too short string for Gain setting"); - return; + return -1; } - gain = (dbus_uint16_t) strtol(&buf[5], NULL, 10); + gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10); if (gain > 15) { error("Invalid gain value received: %u", gain); - return; + return -1; } - switch (buf[3]) { + switch (buf[5]) { case HEADSET_GAIN_SPEAKER: if (device->headset->sp_gain == gain) - return; + goto ok; name = "SpeakerGainChanged"; device->headset->sp_gain = gain; break; case HEADSET_GAIN_MICROPHONE: if (device->headset->mic_gain == gain) - return; + goto ok; name = "MicrophoneGainChanged"; device->headset->mic_gain = gain; break; default: error("Unknown gain setting"); - return; + return G_IO_ERROR_INVAL; } dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, name, - DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID); + AUDIO_HEADSET_INTERFACE, name, + DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID); + +ok: + return headset_send(device->headset, "\r\nOK\r\n"); } -static headset_event_t parse_headset_event(const char *buf, char *rsp, - int rsp_len) +static struct event event_callbacks[] = { + {"ATA", answer_call}, + {"AT+VG", signal_gain_setting}, + {"AT+BRSF", supported_features}, + {"AT+CIND", report_indicators}, + {"AT+CMER", event_reporting}, + {"AT+CHLD", call_hold}, + {"AT+CHUP", terminate_call}, + {"AT+CKPD", answer_call}, + {0} +}; + +static GIOError handle_event(struct device *device, const char *buf) { - printf("Received: %s\n", buf); + struct event *pt; - /* Return an error if this is not a proper AT command */ - if (strncmp(buf, "AT", 2)) { - snprintf(rsp, rsp_len, "\r\nERROR\r\n"); - return HEADSET_EVENT_INVALID; + debug("Received %s", buf); + + for (pt = event_callbacks; pt->cmd; pt++) { + if (!strncmp(buf, pt->cmd, strlen(pt->cmd))) + return pt->callback(device, buf); } - buf += 2; + return -EINVAL; +} - if (!strncmp(buf, "+CKPD", 5)) { - snprintf(rsp, rsp_len, "\r\nOK\r\n"); - return HEADSET_EVENT_KEYPRESS; - } else if (!strncmp(buf, "+VG", 3)) { - snprintf(rsp, rsp_len, "\r\nOK\r\n"); - return HEADSET_EVENT_GAIN; - } else { - snprintf(rsp, rsp_len, "\r\nERROR\r\n"); - return HEADSET_EVENT_UNKNOWN; +static void pending_connect_free(struct pending_connect *c) +{ + if (c->io) { + g_io_channel_close(c->io); + g_io_channel_unref(c->io); } + if (c->msg) + dbus_message_unref(c->msg); + if (c->call) { + dbus_pending_call_cancel(c->call); + dbus_pending_call_unref(c->call); + } + + g_free(c); } static void close_sco(struct device *device) @@ -214,10 +310,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, { struct headset *hs; unsigned char buf[BUF_SIZE]; - char *cr, rsp[BUF_SIZE]; + char *cr; gsize bytes_read = 0; - gsize free_space, count, bytes_written, total_bytes_written; - GIOError err; + gsize free_space; + int err; off_t cmd_len; if (cond & G_IO_NVAL) @@ -253,49 +349,13 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, if (!cr) return TRUE; - cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; + cmd_len = 1 + (off_t) cr - (off_t) &hs->buf[hs->data_start]; *cr = '\0'; - memset(rsp, 0, sizeof(rsp)); - - switch (parse_headset_event(&hs->buf[hs->data_start], rsp, - sizeof(rsp))) { - case HEADSET_EVENT_GAIN: - hs_signal_gain_setting(device, &hs->buf[hs->data_start] + 2); - break; - - case HEADSET_EVENT_KEYPRESS: - if (hs->ring_timer) { - g_source_remove(hs->ring_timer); - hs->ring_timer = 0; - } - - dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, - "AnswerRequested", - DBUS_TYPE_INVALID); - break; - - case HEADSET_EVENT_INVALID: - case HEADSET_EVENT_UNKNOWN: - default: - debug("Unknown headset event"); - break; - } - - count = strlen(rsp); - total_bytes_written = bytes_written = 0; - err = G_IO_ERROR_NONE; - - while (err == G_IO_ERROR_NONE && total_bytes_written < count) { - err = g_io_channel_write(hs->rfcomm, - rsp + total_bytes_written, - count - total_bytes_written, - &bytes_written); - if (err != G_IO_ERROR_NONE) - error("Error while writting to the audio output channel"); - total_bytes_written += bytes_written; - }; + err = handle_event(device, &hs->buf[hs->data_start]); + if (err < 0) + error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start], + strerror(-err), -err); hs->data_start += cmd_len; hs->data_length -= cmd_len; @@ -328,30 +388,6 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static GIOError headset_send(struct headset *hs, const char *str) -{ - GIOError err; - gsize total_written, written, count; - - if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { - error("headset_send: the headset is not connected"); - return G_IO_ERROR_UNKNOWN; - } - - count = strlen(str); - written = total_written = 0; - - while (total_written < count) { - err = g_io_channel_write(hs->rfcomm, str + total_written, - count - total_written, &written); - if (err != G_IO_ERROR_NONE) - return err; - total_written += written; - } - - return G_IO_ERROR_NONE; -} - static void pending_connect_ok(struct pending_connect *c, struct device *dev) { struct headset *hs = dev->headset; @@ -593,6 +629,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; + unsigned int id; c = hs->pending->data; @@ -642,21 +679,11 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - if (hs->type == SVC_HEADSET && - ((uuid.type == SDP_UUID32 && - uuid.value.uuid32 != HEADSET_SVCLASS_ID) || - (uuid.type == SDP_UUID16 && - uuid.value.uuid16 != HEADSET_SVCLASS_ID))) { - error("Service classes did not contain the expected UUID hsp"); - goto failed_not_supported; - } + id = hs->enable_hfp ? HANDSFREE_SVCLASS_ID : HEADSET_SVCLASS_ID; - if (hs->type == SVC_HANDSFREE && - ((uuid.type == SDP_UUID32 && - uuid.value.uuid32 != HANDSFREE_SVCLASS_ID) || - (uuid.type == SDP_UUID16 && - uuid.value.uuid16 != HANDSFREE_SVCLASS_ID))) { - error("Service classes did not contain the expected UUID hfp"); + if ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != id) + || (uuid.type == SDP_UUID16 && uuid.value.uuid16 != id)) { + error("Service classes did not contain the expected UUID"); goto failed_not_supported; } diff --git a/audio/headset.h b/audio/headset.h index b151dbd9..179c8a4a 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -27,13 +27,6 @@ #define DEFAULT_HS_AG_CHANNEL 12 #define DEFAULT_HF_AG_CHANNEL 13 -typedef enum { - HEADSET_EVENT_KEYPRESS, - HEADSET_EVENT_GAIN, - HEADSET_EVENT_UNKNOWN, - HEADSET_EVENT_INVALID -} headset_event_t; - typedef enum { HEADSET_STATE_DISCONNECTED, HEADSET_STATE_CONNECT_IN_PROGRESS, @@ -84,3 +77,4 @@ gboolean headset_lock(struct device *dev, headset_lock_t lock); gboolean headset_unlock(struct device *dev, headset_lock_t lock); gboolean headset_suspend(struct device *dev, void *data); gboolean headset_play(struct device *dev, void *data); + -- cgit From f77777a5ab50a1f49f0717776d92e1337f09422b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 14 Dec 2007 00:06:16 +0000 Subject: Fix mic gain value --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index e7425d5a..737957cc 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1637,7 +1637,7 @@ void headset_set_state(struct device *dev, headset_state_t state) if (hs->mic_gain >= 0) { snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", - hs->sp_gain); + hs->mic_gain); headset_send(hs, str); } break; -- cgit From 4d2920f3901c40b0b8abaace822f6e4a6537fb81 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 14 Dec 2007 14:00:23 +0000 Subject: Prepare for HFP features --- audio/headset.c | 75 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 28 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 737957cc..42e385cd 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,17 @@ #define HEADSET_GAIN_SPEAKER 'S' #define HEADSET_GAIN_MICROPHONE 'M' +#define AG_FEATURE_THREE_WAY_CALLING 0x0001 +#define AG_FEATURE_EC_ANDOR_NR 0x0002 +#define AG_FEATURE_VOICE_RECOGNITION 0x0004 +#define AG_FEATURE_INBAND_RINGTONE 0x0008 +#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG 0x0010 +#define AG_FEATURE_REJECT_A_CALL 0x0020 +#define AG_FEATURE_ENHANCES_CALL_STATUS 0x0040 +#define AG_FEATURE_ENHANCES_CALL_CONTROL 0x0080 +/*Audio Gateway features.Default is In-band Ringtone*/ +static unsigned int ag_features = AG_FEATURE_INBAND_RINGTONE; + static char *str_state[] = { "HEADSET_STATE_DISCONNECTED", "HEADSET_STATE_CONNECT_IN_PROGRESS", @@ -120,24 +132,37 @@ struct event { static int rfcomm_connect(struct device *device, struct pending_connect *c); static int get_handles(struct device *device, struct pending_connect *c); -static int headset_send(struct headset *hs, const char *str) +static int headset_send(struct headset *hs, char *format, ...) { - GIOError err; + char rsp[BUF_SIZE]; + va_list ap; gsize total_written, written, count; + int err; + + va_start(ap, format); + err = vsnprintf(rsp, sizeof(rsp), format, ap); + va_end(ap); + + if (err < 0) + return -EINVAL; if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { error("headset_send: the headset is not connected"); return -EIO; } - count = strlen(str); + count = strlen(rsp); written = total_written = 0; while (total_written < count) { - err = g_io_channel_write(hs->rfcomm, str + total_written, - count - total_written, &written); - if (err != G_IO_ERROR_NONE) + GIOError io_err; + + io_err = g_io_channel_write(hs->rfcomm, rsp + total_written, + count - total_written, + &written); + if (io_err != G_IO_ERROR_NONE) return -errno; + total_written += written; } @@ -150,8 +175,7 @@ static int supported_features(struct device *device, const char *buf) int err; hs->hfp_features = strtoul(&buf[8], NULL, 10); - - err = headset_send(hs, "\r\n+BRSF:0\r\n"); + err = headset_send(hs, "\r\n+BRSF=%u\r\n", ag_features); if (err < 0) return err; @@ -355,7 +379,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = handle_event(device, &hs->buf[hs->data_start]); if (err < 0) error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start], - strerror(-err), -err); + strerror(-err), -err); hs->data_start += cmd_len; hs->data_length -= cmd_len; @@ -1103,8 +1127,11 @@ error: static gboolean ring_timer_cb(gpointer data) { struct device *device = data; + int err; + + err = headset_send(device->headset, "\r\nRING\r\n"); - if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) + if (err) error("Sending RING failed"); return TRUE; @@ -1116,6 +1143,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, struct device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; + int err; if (hs->state < HEADSET_STATE_CONNECTED) return error_not_connected(conn, msg); @@ -1129,7 +1157,8 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, goto done; } - if (headset_send(device->headset, "\r\nRING\r\n") != G_IO_ERROR_NONE) { + err = headset_send(device->headset, "\r\nRING\r\n"); + if (err) { dbus_message_unref(reply); return error_failed(conn, msg, "Failed"); } @@ -1261,7 +1290,7 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, DBusMessage *reply; DBusError derr; dbus_uint16_t gain; - char str[13]; + int err; if (hs->state < HEADSET_STATE_CONNECTED) return error_not_connected(conn, msg); @@ -1286,10 +1315,8 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, if (hs->state != HEADSET_STATE_PLAYING) goto done; - - snprintf(str, sizeof(str) - 1, "\r\n+VG%c=%u\r\n", type, gain); - - if (headset_send(device->headset, str) != G_IO_ERROR_NONE) { + err = headset_send(device->headset, "\r\n+VG%c=%u\r\n", type, gain); + if (err) { dbus_message_unref(reply); return error_failed(conn, msg, "Unable to send to headset"); } @@ -1584,7 +1611,6 @@ int headset_close_rfcomm(struct device *dev) void headset_set_state(struct device *dev, headset_state_t state) { struct headset *hs = dev->headset; - char str[13]; if (hs->state == state) return; @@ -1629,17 +1655,10 @@ void headset_set_state(struct device *dev, headset_state_t state) AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); - if (hs->sp_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGS=%u\r\n", - hs->sp_gain); - headset_send(hs, str); - } - - if (hs->mic_gain >= 0) { - snprintf(str, sizeof(str) - 1, "\r\n+VGM=%u\r\n", - hs->mic_gain); - headset_send(hs, str); - } + if (hs->sp_gain >= 0) + headset_send(hs, "\r\n+VGS=%u\r\n", hs->sp_gain); + if (hs->mic_gain >= 0) + headset_send(hs, "\r\n+VGM=%u\r\n", hs->mic_gain); break; } -- cgit From b0f2b7bae42a94f9bd0250e7050fabbeb3fb5d25 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 14 Dec 2007 14:13:54 +0000 Subject: Remove unecessary variable and strlen call --- audio/headset.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 42e385cd..446c1eb5 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -137,13 +137,12 @@ static int headset_send(struct headset *hs, char *format, ...) char rsp[BUF_SIZE]; va_list ap; gsize total_written, written, count; - int err; va_start(ap, format); - err = vsnprintf(rsp, sizeof(rsp), format, ap); + count = vsnprintf(rsp, sizeof(rsp), format, ap); va_end(ap); - if (err < 0) + if (count < 0) return -EINVAL; if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { @@ -151,7 +150,6 @@ static int headset_send(struct headset *hs, char *format, ...) return -EIO; } - count = strlen(rsp); written = total_written = 0; while (total_written < count) { -- cgit From 71e99ae5555d601df8b90c92e990d9d46e848a96 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 14 Dec 2007 15:30:47 +0000 Subject: Use write instead of g_io_channel_write (which is deprecated) --- audio/headset.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 446c1eb5..c669b513 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -136,7 +136,8 @@ static int headset_send(struct headset *hs, char *format, ...) { char rsp[BUF_SIZE]; va_list ap; - gsize total_written, written, count; + ssize_t total_written, written, count; + int fd; va_start(ap, format); count = vsnprintf(rsp, sizeof(rsp), format, ap); @@ -151,14 +152,11 @@ static int headset_send(struct headset *hs, char *format, ...) } written = total_written = 0; + fd = g_io_channel_unix_get_fd(hs->rfcomm); while (total_written < count) { - GIOError io_err; - - io_err = g_io_channel_write(hs->rfcomm, rsp + total_written, - count - total_written, - &written); - if (io_err != G_IO_ERROR_NONE) + written = write(fd, rsp + total_written, count - total_written); + if (written < 0) return -errno; total_written += written; -- cgit From 72947933a64f92a798567e74e75c0222375ae599 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Sat, 5 Jan 2008 14:51:47 +0000 Subject: use correct variable to pick up alsa prefs --- audio/pcm_bluetooth.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index b0e3838b..4b169522 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -1327,19 +1327,19 @@ static int bluetooth_parse_config(snd_config_t *conf, return -EINVAL; } - if (strcmp(pref, "auto") == 0) { + if (strcmp(mode, "auto") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; bt_config->has_channel_mode = 1; - } else if (strcmp(pref, "mono") == 0) { + } else if (strcmp(mode, "mono") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; bt_config->has_channel_mode = 1; - } else if (strcmp(pref, "dual") == 0) { + } else if (strcmp(mode, "dual") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; bt_config->has_channel_mode = 1; - } else if (strcmp(pref, "stereo") == 0) { + } else if (strcmp(mode, "stereo") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; bt_config->has_channel_mode = 1; - } else if (strcmp(pref, "joint") == 0) { + } else if (strcmp(mode, "joint") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; bt_config->has_channel_mode = 1; } @@ -1352,13 +1352,13 @@ static int bluetooth_parse_config(snd_config_t *conf, return -EINVAL; } - if (strcmp(pref, "auto") == 0) { + if (strcmp(allocation, "auto") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO; bt_config->has_allocation_method = 1; - } else if (strcmp(pref, "loudness") == 0) { + } else if (strcmp(allocation, "loudness") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; bt_config->has_allocation_method = 1; - } else if (strcmp(pref, "snr") == 0) { + } else if (strcmp(allocation, "snr") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR; bt_config->has_allocation_method = 1; } -- cgit From 259598d66abd75124bc1c5f008a504d98e78b847 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 7 Jan 2008 23:52:02 +0000 Subject: don't overwrite the output of bluetooth_init also the memset is done inside bluetooth_init --- audio/ctl_bluetooth.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'audio') diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index 04185b36..f7239f67 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -317,7 +317,6 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) DBG("Bluetooth Control plugin"); data = malloc(sizeof(struct bluetooth_data)); - memset(data, 0, sizeof(struct bluetooth_data)); if (!data) { err = -ENOMEM; goto error; @@ -327,8 +326,6 @@ SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) if (err < 0) goto error; - memset(data, 0, sizeof(*data)); - data->ext.version = SND_CTL_EXT_VERSION; data->ext.card_idx = -1; -- cgit From 4a69d2fface8b45c22050d989ff77e818b6ceca8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jan 2008 08:58:14 +0000 Subject: Improve HFP/HSP discovery and connection state tracking --- audio/headset.c | 39 +++++++++++++++++++++------------------ audio/headset.h | 14 ++++---------- audio/manager.c | 29 ++++++++++++----------------- audio/manager.h | 1 + 4 files changed, 38 insertions(+), 45 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c669b513..4695c46e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -111,8 +111,8 @@ struct headset { int data_start; int data_length; - int enable_hfp; - headset_type_t type; + gboolean hfp_active; + gboolean search_hfp; headset_state_t state; GSList *pending; @@ -604,6 +604,11 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, hs->rfcomm = chan; c->io = NULL; + if (server_is_enabled(HANDSFREE_SVCLASS_ID) && hs->hfp_handle != 0) + hs->hfp_active = TRUE; + else + hs->hfp_active = FALSE; + headset_set_state(device, HEADSET_STATE_CONNECTED); debug("%s: Connected to %s", device->path, hs_address); @@ -699,7 +704,7 @@ static void get_record_reply(DBusPendingCall *call, void *data) goto failed_not_supported; } - id = hs->enable_hfp ? HANDSFREE_SVCLASS_ID : HEADSET_SVCLASS_ID; + id = hs->search_hfp ? HANDSFREE_SVCLASS_ID : HEADSET_SVCLASS_ID; if ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != id) || (uuid.type == SDP_UUID16 && uuid.value.uuid16 != id)) { @@ -809,10 +814,9 @@ static void get_handles_reply(DBusPendingCall *call, void *data) } if (array_len < 1) { - - if(hs->type == SVC_HANDSFREE) { + if (hs->search_hfp) { debug("No record handles found for hfp"); - hs->type = SVC_HEADSET; + hs->search_hfp = FALSE; get_handles(device, c); dbus_message_unref(reply); return; @@ -892,10 +896,10 @@ static int get_handles(struct device *device, struct pending_connect *c) return -EINVAL; } - if (hs->type == SVC_HEADSET) - hs_svc = "hsp"; - else + if (hs->search_hfp) hs_svc = "hfp"; + else + hs_svc = "hsp"; ba2str(&device->dst, hs_address); addr_ptr = hs_address; @@ -931,8 +935,6 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) if (!g_slist_find(hs->pending, c)) hs->pending = g_slist_append(hs->pending, c); - hs->type = hs->enable_hfp ? SVC_HANDSFREE : SVC_HEADSET; - if (hs->state == HEADSET_STATE_DISCONNECTED) return get_handles(device, c); else @@ -1440,8 +1442,8 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) headset_set_channel(headset, record); } -struct headset *headset_init(struct device *dev, int enable_hfp, - sdp_record_t *record, uint16_t svc) +struct headset *headset_init(struct device *dev, sdp_record_t *record, + uint16_t svc) { struct headset *hs; @@ -1449,7 +1451,8 @@ struct headset *headset_init(struct device *dev, int enable_hfp, hs->rfcomm_ch = -1; hs->sp_gain = -1; hs->mic_gain = -1; - hs->enable_hfp = enable_hfp; + hs->search_hfp = server_is_enabled(HANDSFREE_SVCLASS_ID); + hs->hfp_active = FALSE; if (!record) goto register_iface; @@ -1561,18 +1564,18 @@ error: return 0; } -headset_type_t headset_get_type(struct device *dev) +gboolean get_hfp_active(struct device *dev) { struct headset *hs = dev->headset; - return hs->type; + return hs->hfp_active; } -void headset_set_type(struct device *dev, headset_type_t type) +void set_hfp_active(struct device *dev, gboolean active) { struct headset *hs = dev->headset; - hs->type = type; + hs->hfp_active = active; } int headset_connect_rfcomm(struct device *dev, int sock) diff --git a/audio/headset.h b/audio/headset.h index 179c8a4a..4869bb36 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -35,11 +35,6 @@ typedef enum { HEADSET_STATE_PLAYING } headset_state_t; -typedef enum { - SVC_HEADSET, - SVC_HANDSFREE -} headset_type_t; - typedef enum { HEADSET_LOCK_READ = 1, HEADSET_LOCK_WRITE = 1 << 1, @@ -47,8 +42,8 @@ typedef enum { typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data); -struct headset *headset_init(struct device *dev, int enable_hfp, - sdp_record_t *record, uint16_t svc); +struct headset *headset_init(struct device *dev, sdp_record_t *record, + uint16_t svc); void headset_free(struct device *dev); @@ -58,8 +53,8 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, void *user_data); gboolean headset_cancel_stream(struct device *dev, unsigned int id); -headset_type_t headset_get_type(struct device *dev); -void headset_set_type(struct device *dev, headset_type_t type); +gboolean get_hfp_active(struct device *dev); +void set_hfp_active(struct device *dev, gboolean active); int headset_connect_rfcomm(struct device *dev, int sock); int headset_close_rfcomm(struct device *dev); @@ -77,4 +72,3 @@ gboolean headset_lock(struct device *dev, headset_lock_t lock); gboolean headset_unlock(struct device *dev, headset_lock_t lock); gboolean headset_suspend(struct device *dev, void *data); gboolean headset_play(struct device *dev, void *data); - diff --git a/audio/manager.c b/audio/manager.c index cfadec47..cd4ba873 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -108,7 +108,6 @@ static GIOChannel *hs_server = NULL; static GIOChannel *hf_server = NULL; static const struct enabled_interfaces *enabled; -static gboolean enable_hfp = FALSE; static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, @@ -214,7 +213,7 @@ done: return uuid16; } -static gboolean server_is_enabled(uint16_t svc) +gboolean server_is_enabled(uint16_t svc) { gboolean ret; @@ -255,7 +254,6 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - enable_hfp, record, uuid16); break; case HEADSET_AGW_SVCLASS_ID: @@ -267,7 +265,6 @@ static void handle_record(sdp_record_t *record, struct device *device) headset_update(device, record, uuid16); else device->headset = headset_init(device, - enable_hfp, record, uuid16); break; case HANDSFREE_AGW_SVCLASS_ID: @@ -514,8 +511,8 @@ static void get_handles_reply(DBusPendingCall *call, error("GetRemoteServiceHandles failed: %s", derr.message); if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - error_connection_attempt_failed(connection, data->msg, - EHOSTDOWN); + error_connection_attempt_failed(connection, data->msg, + EHOSTDOWN); else error_failed(connection, data->msg, derr.message); dbus_error_free(&derr); @@ -648,7 +645,7 @@ struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) if (device->headset) return device; - device->headset = headset_init(device, enable_hfp, NULL, 0); + device->headset = headset_init(device, NULL, 0); if (!device->headset) return NULL; @@ -1073,7 +1070,7 @@ static void parse_stored_devices(char *key, char *value, void *data) bacpy(&device->store, src); if (enabled->headset && strstr(value, "headset")) - device->headset = headset_init(device, enable_hfp, NULL, 0); + device->headset = headset_init(device, NULL, 0); if (enabled->sink && strstr(value, "sink")) device->sink = sink_init(device); if (enabled->control && strstr(value, "control")) @@ -1383,10 +1380,10 @@ static void auth_cb(DBusPendingCall *call, void *data) DBusError err; const char *uuid; - if (headset_get_type(device) == SVC_HEADSET) - uuid = HSP_AG_UUID; - else + if (get_hfp_active(device)) uuid = HFP_AG_UUID; + else + uuid = HSP_AG_UUID; dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { @@ -1419,7 +1416,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) socklen_t size; const char *uuid; struct device *device; - headset_type_t type; + gboolean hfp_active; if (cond & G_IO_NVAL) return FALSE; @@ -1441,10 +1438,10 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) } if (chan == hs_server) { - type = SVC_HEADSET; + hfp_active = FALSE; uuid = HSP_AG_UUID; } else { - type = SVC_HANDSFREE; + hfp_active = TRUE; uuid = HFP_AG_UUID; } @@ -1466,7 +1463,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - headset_set_type(device, type); + set_hfp_active(device, hfp_active); if (!manager_authorize(&device->dst, uuid, auth_cb, device, NULL)) goto failed; @@ -1564,8 +1561,6 @@ static int headset_server_init(DBusConnection *conn, gboolean no_hfp) if (no_hfp) return 0; - enable_hfp = TRUE; - chan = DEFAULT_HF_AG_CHANNEL; hf_server = server_socket(&chan); diff --git a/audio/manager.h b/audio/manager.h index cbcf1672..85fe4881 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -44,6 +44,7 @@ void audio_exit(void); uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); int remove_service_record(DBusConnection *conn, uint32_t rec_id); +gboolean server_is_enabled(uint16_t svc); struct device *manager_find_device(bdaddr_t *bda, const char *interface, gboolean connected); -- cgit From 89bb414fbdefa9b17872b652b1b71bc11edb2947 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jan 2008 14:45:32 +0000 Subject: Further headset fixes and cleanup --- audio/headset.c | 77 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 4695c46e..86286b45 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -175,7 +175,7 @@ static int supported_features(struct device *device, const char *buf) if (err < 0) return err; - return headset_send(device->headset, "\r\nOK\r\n"); + return headset_send(hs, "\r\nOK\r\n"); } static int report_indicators(struct device *device, const char *buf) @@ -192,12 +192,13 @@ static int report_indicators(struct device *device, const char *buf) if (err < 0) return err; - return headset_send(device->headset, "\r\nOK\r\n"); + return headset_send(hs, "\r\nOK\r\n"); } static int event_reporting(struct device *device, const char *buf) { - return headset_send(device->headset, "\r\nOK\r\n"); + struct headset *hs = device->headset; + return headset_send(hs, "\r\nOK\r\n"); } static int call_hold(struct device *device, const char *buf) @@ -209,25 +210,64 @@ static int call_hold(struct device *device, const char *buf) if (err < 0) return err; - return headset_send(device->headset, "\r\nOK\r\n"); + return headset_send(hs, "\r\nOK\r\n"); } static int answer_call(struct device *device, const char *buf) { + struct headset *hs = device->headset; + int err; + dbus_connection_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); - return headset_send(device->headset, "\r\nOK\r\n"); + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } + + if (!hs->hfp_active) + return headset_send(hs, "\r\nOK\r\n"); + + err = headset_send(hs, "\r\nOK\r\n"); + if (err < 0) + return err; + + /*+CIEV: (call = 1)*/ + err = headset_send(hs, "\r\n+CIEV:2, 1\r\n"); + if (err < 0) + return err; + + /*+CIEV: (callsetup = 0)*/ + return headset_send(hs, "\r\n+CIEV:3, 0\r\n"); } static int terminate_call(struct device *device, const char *buf) { - return headset_send(device->headset, "\r\nOK\r\n"); + struct headset *hs = device->headset; + int err; + + dbus_connection_emit_signal(device->conn, device->path, + AUDIO_HEADSET_INTERFACE, "TerminateCall", + DBUS_TYPE_INVALID); + + if (hs->ring_timer) { + g_source_remove(hs->ring_timer); + hs->ring_timer = 0; + } + + err = headset_send(hs, "\r\nOK\r\n"); + if (err < 0) + return err; + + /*+CIEV: (call = 0)*/ + return headset_send(hs, "\r\n+CIEV:2, 0\r\n"); } static int signal_gain_setting(struct device *device, const char *buf) { + struct headset *hs = device->headset; const char *name; dbus_uint16_t gain; @@ -245,16 +285,16 @@ static int signal_gain_setting(struct device *device, const char *buf) switch (buf[5]) { case HEADSET_GAIN_SPEAKER: - if (device->headset->sp_gain == gain) + if (hs->sp_gain == gain) goto ok; name = "SpeakerGainChanged"; - device->headset->sp_gain = gain; + hs->sp_gain = gain; break; case HEADSET_GAIN_MICROPHONE: - if (device->headset->mic_gain == gain) + if (hs->mic_gain == gain) goto ok; name = "MicrophoneGainChanged"; - device->headset->mic_gain = gain; + hs->mic_gain = gain; break; default: error("Unknown gain setting"); @@ -267,7 +307,7 @@ static int signal_gain_setting(struct device *device, const char *buf) DBUS_TYPE_INVALID); ok: - return headset_send(device->headset, "\r\nOK\r\n"); + return headset_send(hs, "\r\nOK\r\n"); } static struct event event_callbacks[] = { @@ -1125,11 +1165,12 @@ error: static gboolean ring_timer_cb(gpointer data) { struct device *device = data; + struct headset *hs = device->headset; int err; - err = headset_send(device->headset, "\r\nRING\r\n"); + err = headset_send(hs, "\r\nRING\r\n"); - if (err) + if (err < 0) error("Sending RING failed"); return TRUE; @@ -1155,8 +1196,8 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, goto done; } - err = headset_send(device->headset, "\r\nRING\r\n"); - if (err) { + err = headset_send(hs, "\r\nRING\r\n"); + if (err < 0) { dbus_message_unref(reply); return error_failed(conn, msg, "Failed"); } @@ -1313,8 +1354,9 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, if (hs->state != HEADSET_STATE_PLAYING) goto done; - err = headset_send(device->headset, "\r\n+VG%c=%u\r\n", type, gain); - if (err) { + + err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain); + if (err < 0) { dbus_message_unref(reply); return error_failed(conn, msg, "Unable to send to headset"); } @@ -1379,6 +1421,7 @@ static DBusSignalVTable headset_signals[] = { { "Playing", "" }, { "SpeakerGainChanged", "q" }, { "MicrophoneGainChanged", "q" }, + { "TerminateCall", "" }, { NULL, NULL } }; -- cgit From 706c30bdf5498c3fce7fae788ee6e3927fba53f6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jan 2008 15:28:10 +0000 Subject: Implement SetupCall method --- audio/headset.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 86286b45..b14261ce 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1397,6 +1397,56 @@ static DBusHandlerResult hs_set_mic_gain(DBusConnection *conn, return hs_set_gain(conn, msg, data, HEADSET_GAIN_MICROPHONE); } +static DBusHandlerResult hf_setup_call(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + struct headset *hs = device->headset; + DBusMessage *reply; + DBusError derr; + const char *value; + int err; + + if (!hs->hfp_active) + return error_not_supported(device->conn, msg); + + if (hs->state < HEADSET_STATE_CONNECTED) + return error_not_connected(conn, msg); + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error_invalid_arguments(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + if (!strncmp(value, "incoming", 8)) + err = headset_send(hs, "\r\n+CIEV:3, 1\r\n"); + else if (!strncmp(value, "outgoing", 8)) + err = headset_send(hs, "\r\n+CIEV:3, 2\r\n"); + else if (!strncmp(value, "remote", 6)) + err = headset_send(hs, "\r\n+CIEV:3, 3\r\n"); + else + err = -EINVAL; + + if (err < 0) { + dbus_message_unref(reply); + return error_failed_errno(conn, msg, -err); + } + + send_message_and_unref(conn, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static DBusMethodVTable headset_methods[] = { { "Connect", hs_connect, "", "" }, { "Disconnect", hs_disconnect, "", "" }, @@ -1410,6 +1460,7 @@ static DBusMethodVTable headset_methods[] = { { "GetMicrophoneGain", hs_get_mic_gain, "", "q" }, { "SetSpeakerGain", hs_set_speaker_gain, "q", "" }, { "SetMicrophoneGain", hs_set_mic_gain, "q", "" }, + { "SetupCall", hf_setup_call, "s", "" }, { NULL, NULL, NULL, NULL } }; -- cgit From 80c7e40837725ad7c33d02b12818b9e69a3b3a4d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jan 2008 15:29:59 +0000 Subject: Update API documentation --- audio/audio-api.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 4fbed84a..a1af06b8 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -29,7 +29,7 @@ Methods void RemoveDevice(string path) [experimental] Removes a device from the device tree. If there are - any connections open to the device they will be closed. + any connections open to the device they will be closed. array{string} ListDevices() [experimental] @@ -183,6 +183,13 @@ Methods void Connect() Changes the current speaker gain if possible. + void SetupCall(string value) + + Sets up an call with the connected HFP. The value can + be "incoming", "outgoing" or "remote" to indicate + incoming call, outgoing call and remote party alerted + respectively. + Signals void AnswerRequested() Sent when the answer button is pressed on the headset @@ -211,6 +218,10 @@ Signals void AnswerRequested() The microphone gain changed. + void TerminateCall() + + Sent when an ongoing call is terminated. + org.bluez.audio.Gateway interface ================================= -- cgit From 466b76b3f3eab30328476746a964be2c5cbfe869 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2008 03:00:22 +0000 Subject: TerminateCall signal should be called CallTerminated --- audio/audio-api.txt | 2 +- audio/headset.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index a1af06b8..da6a7aab 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -218,7 +218,7 @@ Signals void AnswerRequested() The microphone gain changed. - void TerminateCall() + void CallTerminated() Sent when an ongoing call is terminated. diff --git a/audio/headset.c b/audio/headset.c index b14261ce..1a884b65 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -249,7 +249,7 @@ static int terminate_call(struct device *device, const char *buf) int err; dbus_connection_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, "TerminateCall", + AUDIO_HEADSET_INTERFACE, "CallTerminated", DBUS_TYPE_INVALID); if (hs->ring_timer) { @@ -1472,7 +1472,7 @@ static DBusSignalVTable headset_signals[] = { { "Playing", "" }, { "SpeakerGainChanged", "q" }, { "MicrophoneGainChanged", "q" }, - { "TerminateCall", "" }, + { "CallTerminated", "" }, { NULL, NULL } }; -- cgit From 3709666344736da68472f754ac52edc356039058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2008 03:03:32 +0000 Subject: Fix frame_size calculation when there already is data in the buffer --- audio/pcm_bluetooth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 4b169522..7d5de79f 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -786,11 +786,11 @@ static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u", areas->step, areas->first, offset, size, io->nonblock); + frame_size = areas->step / 8; + if (data->count > 0) goto proceed; - frame_size = areas->step / 8; - nrecv = recv(data->stream.fd, data->buffer, data->link_mtu, MSG_WAITALL | (io->nonblock ? MSG_DONTWAIT : 0)); -- cgit From 751bbec3f68b51a8db3160985b62e2d0f7ea969c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jan 2008 12:36:58 +0000 Subject: handle call rejection properly --- audio/headset.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 1a884b65..cef32250 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -252,15 +252,17 @@ static int terminate_call(struct device *device, const char *buf) AUDIO_HEADSET_INTERFACE, "CallTerminated", DBUS_TYPE_INVALID); + err = headset_send(hs, "\r\nOK\r\n"); + if (err < 0) + return err; + if (hs->ring_timer) { g_source_remove(hs->ring_timer); hs->ring_timer = 0; + /*+CIEV: (callsetup = 0)*/ + return headset_send(hs, "\r\n+CIEV:3, 0\r\n"); } - err = headset_send(hs, "\r\nOK\r\n"); - if (err < 0) - return err; - /*+CIEV: (call = 0)*/ return headset_send(hs, "\r\n+CIEV:2, 0\r\n"); } -- cgit From 15ed6000c87ad5a4e314afb86fb98ea8b2dd76c6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 11 Jan 2008 11:19:00 +0000 Subject: Make sure the correct handle value gets updated when parsing HSP/HFP records --- audio/headset.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index cef32250..73c832f2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -696,7 +696,6 @@ static void get_record_reply(DBusPendingCall *call, void *data) struct device *device = data; struct headset *hs = device->headset; struct pending_connect *c; - unsigned int id; c = hs->pending->data; @@ -741,17 +740,23 @@ static void get_record_reply(DBusPendingCall *call, void *data) memcpy(&uuid, classes->data, sizeof(uuid)); - if (!sdp_uuid128_to_uuid(&uuid)) { + if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16) { error("Not a 16 bit UUID"); goto failed_not_supported; } - id = hs->search_hfp ? HANDSFREE_SVCLASS_ID : HEADSET_SVCLASS_ID; - - if ((uuid.type == SDP_UUID32 && uuid.value.uuid32 != id) - || (uuid.type == SDP_UUID16 && uuid.value.uuid16 != id)) { - error("Service classes did not contain the expected UUID"); - goto failed_not_supported; + if (hs->search_hfp) { + if (uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) { + error("Service record didn't contain the HFP UUID"); + goto failed_not_supported; + } + hs->hfp_handle = record->handle; + } else { + if (uuid.value.uuid16 != HEADSET_SVCLASS_ID) { + error("Service record didn't contain the HSP UUID"); + goto failed_not_supported; + } + hs->hsp_handle = record->handle; } if (!sdp_get_access_protos(record, &protos)) { -- cgit From e65858be07f86fe12756c67a20a5f45debfcc250 Mon Sep 17 00:00:00 2001 From: Brad Midgley Date: Mon, 14 Jan 2008 22:24:05 +0000 Subject: coding style --- audio/pcm_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 7d5de79f..e81bbc16 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -1483,7 +1483,7 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, data->stream.fd = -1; sk = bt_audio_service_open(); - if(sk <= 0) { + if (sk <= 0) { err = -errno; goto failed; } -- cgit From 3eb854122f31b55fc4ba56b5ce555c380947f710 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 16 Jan 2008 09:08:26 +0000 Subject: Reorganize audio.conf parsing in a more modular manner --- audio/audio.conf | 12 ++++++ audio/headset.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++- audio/headset.h | 2 + audio/main.c | 111 +++++------------------------------------------------ audio/manager.c | 115 +++++++++++++++++++++++++++++++++++++++++++++---------- audio/manager.h | 5 +-- 6 files changed, 232 insertions(+), 127 deletions(-) (limited to 'audio') diff --git a/audio/audio.conf b/audio/audio.conf index 3fe8965f..ae9742b8 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -20,6 +20,18 @@ # Defaults to false DisableHFP=true +# HFP Gateway features +# Defaults to false +3WayCalling=false +EchoCancelNoiseCancel=false +VoiceRecognition=false +InBandRingtone=false +VoiceTags=false +RejectingCalls=false +EnhancedCallStatus=false +EnhancedCallControl=false +ExtendedErrorResultCodes=false + # Just an example of potential config options for the other interfaces #[A2DP] #SourceCount=2 diff --git a/audio/headset.c b/audio/headset.c index 73c832f2..0fc013d7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -73,8 +73,10 @@ #define AG_FEATURE_REJECT_A_CALL 0x0020 #define AG_FEATURE_ENHANCES_CALL_STATUS 0x0040 #define AG_FEATURE_ENHANCES_CALL_CONTROL 0x0080 +#define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100 /*Audio Gateway features.Default is In-band Ringtone*/ -static unsigned int ag_features = AG_FEATURE_INBAND_RINGTONE; +static unsigned int ag_features; +static gboolean sco_hci = TRUE; static char *str_state[] = { "HEADSET_STATE_DISCONNECTED", @@ -1586,6 +1588,116 @@ register_iface: return hs; } +int headset_config_init(GKeyFile *config) +{ + GError *err = NULL; + gboolean value; + char *str; + + /* Use the default values if there is no config file */ + if (config == NULL) + return 0; + + str = g_key_file_get_string(config, "General", "SCORouting", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + if (strcmp(str, "PCM") == 0) + sco_hci = FALSE; + else if (strcmp(str, "HCI") == 0) + sco_hci = TRUE; + else + error("Invalid Headset Routing value: %s", str); + g_free(str); + } + + value = g_key_file_get_boolean(config, "Headset", "3WayCalling", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_THREE_WAY_CALLING; + + value = g_key_file_get_boolean(config, "Headset", "EchoCancelNoiseCancel", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_EC_ANDOR_NR; + + value = g_key_file_get_boolean(config, "Headset", "VoiceRecognition", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_VOICE_RECOGNITION; + + value = g_key_file_get_boolean(config, "Headset", "InBandRingtone", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_INBAND_RINGTONE; + + value = g_key_file_get_boolean(config, "Headset", "VoiceTags", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG; + + value = g_key_file_get_boolean(config, "Headset", "RejectingCalls", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_REJECT_A_CALL; + + value = g_key_file_get_boolean(config, "Headset", "EnhancedCallStatus", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_ENHANCES_CALL_STATUS; + + value = g_key_file_get_boolean(config, "Headset", "EnhancedCallControl", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_ENHANCES_CALL_CONTROL; + + value = g_key_file_get_boolean(config, "Headset", + "ExtendedErrorResultCodes", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else if (value) + ag_features |= AG_FEATURE_EXTENDED_ERROR_RESULT_CODES; + + return 0; +} + void headset_free(struct device *dev) { struct headset *hs = dev->headset; diff --git a/audio/headset.h b/audio/headset.h index 4869bb36..a70ad973 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -47,6 +47,8 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record, void headset_free(struct device *dev); +int headset_config_init(GKeyFile *config); + void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, diff --git a/audio/main.c b/audio/main.c index 52396beb..75cb67e6 100644 --- a/audio/main.c +++ b/audio/main.c @@ -42,32 +42,17 @@ #include "device.h" #include "manager.h" -static gboolean disable_hfp = TRUE; -static gboolean sco_hci = TRUE; -static int source_count = 1; - static GMainLoop *main_loop = NULL; -static struct enabled_interfaces enabled = { - .headset = TRUE, - .gateway = FALSE, - .sink = TRUE, - .source = FALSE, - .control = TRUE, - .target = FALSE, -}; - static void sig_term(int sig) { g_main_loop_quit(main_loop); } -static void read_config(const char *file) +static GKeyFile *load_config_file(const char *file) { - GKeyFile *keyfile; GError *err = NULL; - gboolean no_hfp; - char *str; + GKeyFile *keyfile; keyfile = g_key_file_new(); @@ -75,96 +60,17 @@ static void read_config(const char *file) error("Parsing %s failed: %s", file, err->message); g_error_free(err); g_key_file_free(keyfile); - return; - } - - str = g_key_file_get_string(keyfile, "General", "SCORouting", &err); - if (err) { - debug("%s: %s", file, err->message); - g_error_free(err); - err = NULL; - } else { - if (strcmp(str, "PCM") == 0) - sco_hci = FALSE; - else if (strcmp(str, "HCI") == 0) - sco_hci = TRUE; - else - error("Invalid Headset Routing value: %s", str); - g_free(str); - } - - str = g_key_file_get_string(keyfile, "General", "Enable", &err); - if (err) { - debug("%s: %s", file, err->message); - g_error_free(err); - err = NULL; - } else { - if (strstr(str, "Headset")) - enabled.headset = TRUE; - if (strstr(str, "Gateway")) - enabled.gateway = TRUE; - if (strstr(str, "Sink")) - enabled.sink = TRUE; - if (strstr(str, "Source")) - enabled.source = TRUE; - if (strstr(str, "Control")) - enabled.control = TRUE; - if (strstr(str, "Target")) - enabled.target = TRUE; - g_free(str); + return NULL; } - str = g_key_file_get_string(keyfile, "General", "Disable", &err); - if (err) { - debug("%s: %s", file, err->message); - g_error_free(err); - err = NULL; - } else { - if (strstr(str, "Headset")) - enabled.headset = FALSE; - if (strstr(str, "Gateway")) - enabled.gateway = FALSE; - if (strstr(str, "Sink")) - enabled.sink = FALSE; - if (strstr(str, "Source")) - enabled.source = FALSE; - if (strstr(str, "Control")) - enabled.control = FALSE; - if (strstr(str, "Target")) - enabled.target = FALSE; - g_free(str); - } - - no_hfp = g_key_file_get_boolean(keyfile, "Headset", "DisableHFP", - &err); - if (err) { - debug("%s: %s", file, err->message); - g_error_free(err); - err = NULL; - } else - disable_hfp = no_hfp; - - str = g_key_file_get_string(keyfile, "A2DP", "SourceCount", &err); - if (err) { - debug("%s: %s", file, err->message); - g_error_free(err); - err = NULL; - } else { - source_count = atoi(str); - g_free(str); - } - - debug("Config options: DisableHFP=%s, SCORouting=%s, SourceCount=%d", - disable_hfp ? "true" : "false", - sco_hci ? "HCI" : "PCM", source_count); - - g_key_file_free(keyfile); + return keyfile; } int main(int argc, char *argv[]) { DBusConnection *conn; struct sigaction sa; + GKeyFile *config; start_logging("audio", "Bluetooth Audio daemon"); @@ -180,7 +86,7 @@ int main(int argc, char *argv[]) enable_debug(); - read_config(CONFIGDIR "/audio.conf"); + config = load_config_file(CONFIGDIR "/audio.conf"); main_loop = g_main_loop_new(NULL, FALSE); @@ -195,14 +101,15 @@ int main(int argc, char *argv[]) exit(1); } - if (audio_init(conn, &enabled, disable_hfp, sco_hci, - source_count) < 0) { + if (audio_init(conn, config) < 0) { error("Audio init failed!"); exit(1); } if (argc > 1 && !strcmp(argv[1], "-s")) register_external_service(conn, "audio", "Audio service", ""); + if (config) + g_key_file_free(config); g_main_loop_run(main_loop); diff --git a/audio/manager.c b/audio/manager.c index cd4ba873..dd580f8c 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -107,7 +107,13 @@ static uint32_t hf_record_id = 0; static GIOChannel *hs_server = NULL; static GIOChannel *hf_server = NULL; -static const struct enabled_interfaces *enabled; +static struct enabled_interfaces enabled = { + .headset = TRUE, + .gateway = FALSE, + .sink = TRUE, + .source = FALSE, + .control = TRUE, +}; static void get_next_record(struct audio_sdp_data *data); static DBusHandlerResult get_handles(const char *uuid, @@ -225,10 +231,10 @@ gboolean server_is_enabled(uint16_t svc) ret = (hf_server != NULL); break; case AUDIO_SINK_SVCLASS_ID: - return enabled->sink; + return enabled.sink; case AV_REMOTE_TARGET_SVCLASS_ID: case AV_REMOTE_SVCLASS_ID: - return enabled->control; + return enabled.control; default: ret = FALSE; break; @@ -1069,11 +1075,11 @@ static void parse_stored_devices(char *key, char *value, void *data) /* Change storage to source adapter */ bacpy(&device->store, src); - if (enabled->headset && strstr(value, "headset")) + if (enabled.headset && strstr(value, "headset")) device->headset = headset_init(device, NULL, 0); - if (enabled->sink && strstr(value, "sink")) + if (enabled.sink && strstr(value, "sink")) device->sink = sink_init(device); - if (enabled->control && strstr(value, "control")) + if (enabled.control && strstr(value, "control")) device->control = control_init(device); add_device(device, FALSE); } @@ -1529,12 +1535,14 @@ static GIOChannel *server_socket(uint8_t *channel) return io; } -static int headset_server_init(DBusConnection *conn, gboolean no_hfp) +static int headset_server_init(DBusConnection *conn, GKeyFile *config) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; sdp_buf_t buf; + gboolean no_hfp = FALSE; + GError *err = NULL; - if (!(enabled->headset || enabled->gateway)) + if (!(enabled.headset || enabled.gateway)) return 0; hs_server = server_socket(&chan); @@ -1556,7 +1564,17 @@ static int headset_server_init(DBusConnection *conn, gboolean no_hfp) } g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) ag_io_cb, NULL); + (GIOFunc) ag_io_cb, NULL); + + if (config) { + no_hfp = g_key_file_get_boolean(config, "Headset", "DisableHFP", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } + } if (no_hfp) return 0; @@ -1582,7 +1600,7 @@ static int headset_server_init(DBusConnection *conn, gboolean no_hfp) } g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) ag_io_cb, NULL); + (GIOFunc) ag_io_cb, NULL); return 0; } @@ -1610,14 +1628,55 @@ static void server_exit(void) } } -int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, - gboolean no_hfp, gboolean sco_hci, int source_count) +int audio_init(DBusConnection *conn, GKeyFile *config) { int sinks, sources; + char *str; + GError *err = NULL; connection = dbus_connection_ref(conn); - enabled = enable; + if (config) { + str = g_key_file_get_string(config, "General", "Enable", &err); + + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + if (strstr(str, "Headset")) + enabled.headset = TRUE; + if (strstr(str, "Gateway")) + enabled.gateway = TRUE; + if (strstr(str, "Sink")) + enabled.sink = TRUE; + if (strstr(str, "Source")) + enabled.source = TRUE; + if (strstr(str, "Control")) + enabled.control = TRUE; + g_free(str); + } + + str = g_key_file_get_string(config, "General", "Disable", &err); + + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + if (strstr(str, "Headset")) + enabled.headset = FALSE; + if (strstr(str, "Gateway")) + enabled.gateway = FALSE; + if (strstr(str, "Sink")) + enabled.sink = FALSE; + if (strstr(str, "Source")) + enabled.source = FALSE; + if (strstr(str, "Control")) + enabled.control = FALSE; + g_free(str); + } + } if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, NULL, manager_unregister)) { @@ -1625,15 +1684,31 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, goto failed; } - if (headset_server_init(conn, no_hfp) < 0) - goto failed; + if (enabled.headset) { + if (headset_config_init(config) < 0) + goto failed; - if (enable->sink) - sources = source_count; - else + if (headset_server_init(conn, config) < 0) + goto failed; + } + + if (enabled.sink) { + if (config) { + str = g_key_file_get_string(config, "A2DP", + "SourceCount", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + sources = atoi(str); + g_free(str); + } + } + } else sources = 0; - if (enable->source) + if (enabled.source) sinks = 1; else sinks = 0; @@ -1641,7 +1716,7 @@ int audio_init(DBusConnection *conn, struct enabled_interfaces *enable, if (a2dp_init(conn, sources, sinks) < 0) goto failed; - if (enable->control && avrcp_init(conn) < 0) + if (enabled.control && avrcp_init(conn) < 0) goto failed; if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, diff --git a/audio/manager.h b/audio/manager.h index 85fe4881..e740a524 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -32,14 +32,11 @@ struct enabled_interfaces { gboolean sink; gboolean source; gboolean control; - gboolean target; }; typedef void (*create_dev_cb_t) (struct device *dev, void *user_data); -int audio_init(DBusConnection *conn, struct enabled_interfaces *enabled, - gboolean no_hfp, gboolean sco_hci, int source_count); - +int audio_init(DBusConnection *conn, GKeyFile *config); void audio_exit(void); uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); -- 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') 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 10ee2488a526d269d9f50f2737a7401a11881f2c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2008 12:24:32 +0000 Subject: Add (experimental) Headset.IdentifyCall method --- audio/audio-api.txt | 9 +++++- audio/headset.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index da6a7aab..008a194d 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -183,13 +183,20 @@ Methods void Connect() Changes the current speaker gain if possible. - void SetupCall(string value) + void SetupCall(string value) [experimental] Sets up an call with the connected HFP. The value can be "incoming", "outgoing" or "remote" to indicate incoming call, outgoing call and remote party alerted respectively. + void IdentifyCall(string phone_number, int32 type) [experimental] + + Enables a called subscriber to get the calling + line identity (CLI) of the calling party when + receiving a call. The value of type shud be + the same as provided by the GSM stack. + Signals void AnswerRequested() Sent when the answer button is pressed on the headset diff --git a/audio/headset.c b/audio/headset.c index 0fc013d7..a1626d4e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -115,6 +115,9 @@ struct headset { gboolean hfp_active; gboolean search_hfp; + gboolean cli_active; + char *ph_number; + int type; headset_state_t state; GSList *pending; @@ -232,6 +235,11 @@ static int answer_call(struct device *device, const char *buf) if (!hs->hfp_active) return headset_send(hs, "\r\nOK\r\n"); + if (hs->ph_number) { + g_free(hs->ph_number); + hs->ph_number = NULL; + } + err = headset_send(hs, "\r\nOK\r\n"); if (err < 0) return err; @@ -258,6 +266,11 @@ static int terminate_call(struct device *device, const char *buf) if (err < 0) return err; + if (hs->ph_number) { + g_free(hs->ph_number); + hs->ph_number = NULL; + } + if (hs->ring_timer) { g_source_remove(hs->ring_timer); hs->ring_timer = 0; @@ -269,6 +282,18 @@ static int terminate_call(struct device *device, const char *buf) return headset_send(hs, "\r\n+CIEV:2, 0\r\n"); } +static int cli_notification(struct device *device, const char *buf) +{ + struct headset *hs = device->headset; + + if (buf[8] == '1') + hs->cli_active = TRUE; + else + hs->cli_active = FALSE; + + return headset_send(hs, "\r\nOK\r\n"); +} + static int signal_gain_setting(struct device *device, const char *buf) { struct headset *hs = device->headset; @@ -323,6 +348,7 @@ static struct event event_callbacks[] = { {"AT+CHLD", call_hold}, {"AT+CHUP", terminate_call}, {"AT+CKPD", answer_call}, + {"AT+CLIP", cli_notification}, {0} }; @@ -1180,7 +1206,17 @@ static gboolean ring_timer_cb(gpointer data) err = headset_send(hs, "\r\nRING\r\n"); if (err < 0) - error("Sending RING failed"); + error("Error while sending RING: %s (%d)", + strerror(-err), -err); + + if (hs->cli_active && hs->ph_number) { + err = headset_send(hs, "\r\n+CLIP:\"%s\", %d\r\n", + hs->ph_number, hs->type); + + if (err < 0) + error("Error while sending CLIP: %s (%d)", + strerror(-err), -err); + } return TRUE; } @@ -1208,7 +1244,16 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, err = headset_send(hs, "\r\nRING\r\n"); if (err < 0) { dbus_message_unref(reply); - return error_failed(conn, msg, "Failed"); + return error_failed_errno(conn, msg, -err); + } + + if (hs->cli_active && hs->ph_number) { + err = headset_send(hs, "\r\n+CLIP:\"%s\", %d\r\n", + hs->ph_number, hs->type); + if (err < 0) { + dbus_message_unref(reply); + return error_failed_errno(conn, msg, -err); + } } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); @@ -1456,6 +1501,47 @@ static DBusHandlerResult hf_setup_call(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } +static DBusHandlerResult hf_identify_call(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct device *device = data; + struct headset *hs = device->headset; + DBusMessage *reply; + DBusError derr; + const char *number; + dbus_int32_t type; + + if (!hs->hfp_active && !hs->cli_active) + return error_not_supported(device->conn, msg); + + if (hs->state < HEADSET_STATE_CONNECTED) + return error_not_connected(conn, msg); + + dbus_error_init(&derr); + dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &number, + DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&derr)) { + error_invalid_arguments(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + g_free(hs->ph_number); + + hs->ph_number = g_strdup(number); + hs->type = type; + + send_message_and_unref(conn, reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + static DBusMethodVTable headset_methods[] = { { "Connect", hs_connect, "", "" }, { "Disconnect", hs_disconnect, "", "" }, @@ -1470,6 +1556,7 @@ static DBusMethodVTable headset_methods[] = { { "SetSpeakerGain", hs_set_speaker_gain, "q", "" }, { "SetMicrophoneGain", hs_set_mic_gain, "q", "" }, { "SetupCall", hf_setup_call, "s", "" }, + { "IdentifyCall", hf_identify_call, "si", "" }, { NULL, NULL, NULL, NULL } }; @@ -1556,6 +1643,8 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record, hs->mic_gain = -1; hs->search_hfp = server_is_enabled(HANDSFREE_SVCLASS_ID); hs->hfp_active = FALSE; + hs->cli_active = FALSE; + hs->ph_number = NULL; if (!record) goto register_iface; -- cgit From af43115ad952518c6124c9f2e2c48f87b164e97f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2008 13:09:35 +0000 Subject: Fix coding style --- audio/headset.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a1626d4e..2b343d0d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -286,10 +286,7 @@ static int cli_notification(struct device *device, const char *buf) { struct headset *hs = device->headset; - if (buf[8] == '1') - hs->cli_active = TRUE; - else - hs->cli_active = FALSE; + hs->cli_active = buf[8] == '1' ? TRUE : FALSE; return headset_send(hs, "\r\nOK\r\n"); } @@ -340,16 +337,16 @@ ok: } static struct event event_callbacks[] = { - {"ATA", answer_call}, - {"AT+VG", signal_gain_setting}, - {"AT+BRSF", supported_features}, - {"AT+CIND", report_indicators}, - {"AT+CMER", event_reporting}, - {"AT+CHLD", call_hold}, - {"AT+CHUP", terminate_call}, - {"AT+CKPD", answer_call}, - {"AT+CLIP", cli_notification}, - {0} + { "ATA", answer_call }, + { "AT+VG", signal_gain_setting }, + { "AT+BRSF", supported_features }, + { "AT+CIND", report_indicators }, + { "AT+CMER", event_reporting }, + { "AT+CHLD", call_hold }, + { "AT+CHUP", terminate_call }, + { "AT+CKPD", answer_call }, + { "AT+CLIP", cli_notification }, + { 0 } }; static GIOError handle_event(struct device *device, const char *buf) -- cgit From 4f47f8ab1c25fb08b37bafe97a88def3e749098c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 22 Jan 2008 18:01:19 +0000 Subject: Initialize HFP supported features properly --- audio/headset.c | 13 +++++++------ audio/headset.h | 2 +- audio/manager.c | 15 ++++++++------- 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 2b343d0d..d2dc7416 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -74,8 +74,9 @@ #define AG_FEATURE_ENHANCES_CALL_STATUS 0x0040 #define AG_FEATURE_ENHANCES_CALL_CONTROL 0x0080 #define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100 -/*Audio Gateway features.Default is In-band Ringtone*/ -static unsigned int ag_features; + +static uint32_t ag_features = 0; + static gboolean sco_hci = TRUE; static char *str_state[] = { @@ -84,7 +85,7 @@ static char *str_state[] = { "HEADSET_STATE_CONNECTED", "HEADSET_STATE_PLAY_IN_PROGRESS", "HEADSET_STATE_PLAYING", - }; +}; struct pending_connect { DBusMessage *msg; @@ -1674,7 +1675,7 @@ register_iface: return hs; } -int headset_config_init(GKeyFile *config) +uint32_t headset_config_init(GKeyFile *config) { GError *err = NULL; gboolean value; @@ -1682,7 +1683,7 @@ int headset_config_init(GKeyFile *config) /* Use the default values if there is no config file */ if (config == NULL) - return 0; + return ag_features; str = g_key_file_get_string(config, "General", "SCORouting", &err); @@ -1781,7 +1782,7 @@ int headset_config_init(GKeyFile *config) } else if (value) ag_features |= AG_FEATURE_EXTENDED_ERROR_RESULT_CODES; - return 0; + return ag_features; } void headset_free(struct device *dev) diff --git a/audio/headset.h b/audio/headset.h index a70ad973..67155f73 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -47,7 +47,7 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record, void headset_free(struct device *dev); -int headset_config_init(GKeyFile *config); +uint32_t headset_config_init(GKeyFile *config); void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); diff --git a/audio/manager.c b/audio/manager.c index dd580f8c..931af069 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1226,7 +1226,7 @@ static int hsp_ag_record(sdp_buf_t *buf, uint8_t ch) return ret; } -static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch) +static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch, uint32_t feat) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; @@ -1234,9 +1234,9 @@ static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch) sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; sdp_record_t record; - uint16_t u16 = 0x0009; sdp_data_t *channel, *features; uint8_t netid = 0x01; + uint16_t sdpfeat; sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); int ret; @@ -1267,7 +1267,8 @@ static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch) proto[1] = sdp_list_append(proto[1], channel); apseq = sdp_list_append(apseq, proto[1]); - features = sdp_data_alloc(SDP_UINT16, &u16); + sdpfeat = (uint16_t) feat & 0xF; + features = sdp_data_alloc(SDP_UINT16, &sdpfeat); sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); aproto = sdp_list_append(0, apseq); @@ -1541,6 +1542,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) sdp_buf_t buf; gboolean no_hfp = FALSE; GError *err = NULL; + uint32_t features; if (!(enabled.headset || enabled.gateway)) return 0; @@ -1585,7 +1587,9 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (!hf_server) return -1; - if (hfp_ag_record(&buf, chan) < 0) { + features = headset_config_init(config); + + if (hfp_ag_record(&buf, chan, features) < 0) { error("Unable to allocate new service record"); return -1; } @@ -1685,9 +1689,6 @@ int audio_init(DBusConnection *conn, GKeyFile *config) } if (enabled.headset) { - if (headset_config_init(config) < 0) - goto failed; - if (headset_server_init(conn, config) < 0) goto failed; } -- cgit From b8e9425de9f30f87f7315fe9b5ca76348de16f44 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 23 Jan 2008 12:10:45 +0000 Subject: Reply with ERROR to unknown headset commands --- audio/headset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index d2dc7416..fbc66371 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -441,7 +441,10 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, *cr = '\0'; err = handle_event(device, &hs->buf[hs->data_start]); - if (err < 0) + if (err = -EINVAL) { + error("Received unknown command: %s", &hs->buf[hs->data_start]); + err = headset_send(hs, "\r\nERROR\r\n"); + } else if (err < 0) error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start], strerror(-err), -err); -- cgit From a838b6a09d936225aa7c19aaaabbcc9f37fffdce Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 23 Jan 2008 12:13:30 +0000 Subject: Fix typo --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index fbc66371..27a1a66a 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -441,7 +441,7 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, *cr = '\0'; err = handle_event(device, &hs->buf[hs->data_start]); - if (err = -EINVAL) { + if (err == -EINVAL) { error("Received unknown command: %s", &hs->buf[hs->data_start]); err = headset_send(hs, "\r\nERROR\r\n"); } else if (err < 0) -- cgit From f67d81062c4be2cec6bb1b340a9f212f1227e062 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 23 Jan 2008 12:24:16 +0000 Subject: Fix error code handling and minor coding style tweaks --- audio/headset.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 27a1a66a..e655672c 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -300,14 +300,14 @@ static int signal_gain_setting(struct device *device, const char *buf) if (strlen(buf) < 8) { error("Too short string for Gain setting"); - return -1; + return -EINVAL; } gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10); if (gain > 15) { error("Invalid gain value received: %u", gain); - return -1; + return -EINVAL; } switch (buf[5]) { @@ -325,7 +325,7 @@ static int signal_gain_setting(struct device *device, const char *buf) break; default: error("Unknown gain setting"); - return G_IO_ERROR_INVAL; + return -EINVAL; } dbus_connection_emit_signal(device->conn, device->path, @@ -350,15 +350,15 @@ static struct event event_callbacks[] = { { 0 } }; -static GIOError handle_event(struct device *device, const char *buf) +static int handle_event(struct device *device, const char *buf) { - struct event *pt; + struct event *ev; debug("Received %s", buf); - for (pt = event_callbacks; pt->cmd; pt++) { - if (!strncmp(buf, pt->cmd, strlen(pt->cmd))) - return pt->callback(device, buf); + for (ev = event_callbacks; ev->cmd; ev++) { + if (!strncmp(buf, ev->cmd, strlen(ev->cmd))) + return ev->callback(device, buf); } return -EINVAL; @@ -412,9 +412,8 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, if (cond & (G_IO_ERR | G_IO_HUP)) goto failed; - err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, - &bytes_read); - if (err != G_IO_ERROR_NONE) + if (g_io_channel_read(chan, (gchar *) buf, sizeof(buf) - 1, + &bytes_read) != G_IO_ERROR_NONE) return TRUE; free_space = sizeof(hs->buf) - hs->data_start - hs->data_length - 1; @@ -442,11 +441,12 @@ static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, err = handle_event(device, &hs->buf[hs->data_start]); if (err == -EINVAL) { - error("Received unknown command: %s", &hs->buf[hs->data_start]); + error("Badly formated or unrecognized command: %s", + &hs->buf[hs->data_start]); err = headset_send(hs, "\r\nERROR\r\n"); } else if (err < 0) - error("Error handling command %s: %s (%d)", &hs->buf[hs->data_start], - strerror(-err), -err); + error("Error handling command %s: %s (%d)", + &hs->buf[hs->data_start], strerror(-err), -err); hs->data_start += cmd_len; hs->data_length -= cmd_len; -- cgit From 9ae63b37bb909c66a931ee3934af1ad87047f326 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:11:07 +0000 Subject: Initial support of mpeg12 codec. --- audio/a2dp.c | 30 ++++++++++----------- audio/a2dp.h | 77 ++++++++++++++++++++++++++++++++++++++-------------- audio/ipc.h | 8 +++++- audio/sink.c | 88 ++++++++++++++++++++++++++++++------------------------------ audio/unix.c | 19 ++++++++++--- 5 files changed, 139 insertions(+), 83 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 74699cd5..1c59374f 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -355,25 +355,25 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - sbc_cap.frequency = ( A2DP_SAMPLING_FREQ_48000 | - A2DP_SAMPLING_FREQ_44100 | - A2DP_SAMPLING_FREQ_32000 | - A2DP_SAMPLING_FREQ_16000 ); + sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 | + SBC_SAMPLING_FREQ_44100 | + SBC_SAMPLING_FREQ_32000 | + SBC_SAMPLING_FREQ_16000 ); - sbc_cap.channel_mode = ( A2DP_CHANNEL_MODE_JOINT_STEREO | - A2DP_CHANNEL_MODE_STEREO | - A2DP_CHANNEL_MODE_DUAL_CHANNEL | - A2DP_CHANNEL_MODE_MONO ); + sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO | + SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_DUAL_CHANNEL | + SBC_CHANNEL_MODE_MONO ); - sbc_cap.block_length = ( A2DP_BLOCK_LENGTH_16 | - A2DP_BLOCK_LENGTH_12 | - A2DP_BLOCK_LENGTH_8 | - A2DP_BLOCK_LENGTH_4 ); + sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 | + SBC_BLOCK_LENGTH_12 | + SBC_BLOCK_LENGTH_8 | + SBC_BLOCK_LENGTH_4 ); - sbc_cap.subbands = ( A2DP_SUBBANDS_8 | A2DP_SUBBANDS_4 ); + sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 ); - sbc_cap.allocation_method = ( A2DP_ALLOCATION_LOUDNESS | - A2DP_ALLOCATION_SNR ); + sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS | + SBC_ALLOCATION_SNR ); sbc_cap.min_bitpool = MIN_BITPOOL; sbc_cap.max_bitpool = MAX_BITPOOL; diff --git a/audio/a2dp.h b/audio/a2dp.h index 8227296f..cfd1c47d 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -27,26 +27,37 @@ #define A2DP_CODEC_MPEG24 0x02 #define A2DP_CODEC_ATRAC 0x03 -#define A2DP_SAMPLING_FREQ_16000 (1 << 3) -#define A2DP_SAMPLING_FREQ_32000 (1 << 2) -#define A2DP_SAMPLING_FREQ_44100 (1 << 1) -#define A2DP_SAMPLING_FREQ_48000 1 - -#define A2DP_CHANNEL_MODE_MONO (1 << 3) -#define A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define A2DP_CHANNEL_MODE_STEREO (1 << 1) -#define A2DP_CHANNEL_MODE_JOINT_STEREO 1 - -#define A2DP_BLOCK_LENGTH_4 (1 << 3) -#define A2DP_BLOCK_LENGTH_8 (1 << 2) -#define A2DP_BLOCK_LENGTH_12 (1 << 1) -#define A2DP_BLOCK_LENGTH_16 1 - -#define A2DP_SUBBANDS_4 (1 << 1) -#define A2DP_SUBBANDS_8 1 - -#define A2DP_ALLOCATION_SNR (1 << 1) -#define A2DP_ALLOCATION_LOUDNESS 1 +#define SBC_SAMPLING_FREQ_16000 (1 << 3) +#define SBC_SAMPLING_FREQ_32000 (1 << 2) +#define SBC_SAMPLING_FREQ_44100 (1 << 1) +#define SBC_SAMPLING_FREQ_48000 1 + +#define SBC_CHANNEL_MODE_MONO (1 << 3) +#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define SBC_CHANNEL_MODE_STEREO (1 << 1) +#define SBC_CHANNEL_MODE_JOINT_STEREO 1 + +#define SBC_BLOCK_LENGTH_4 (1 << 3) +#define SBC_BLOCK_LENGTH_8 (1 << 2) +#define SBC_BLOCK_LENGTH_12 (1 << 1) +#define SBC_BLOCK_LENGTH_16 1 + +#define SBC_SUBBANDS_4 (1 << 1) +#define SBC_SUBBANDS_8 1 + +#define SBC_ALLOCATION_SNR (1 << 1) +#define SBC_ALLOCATION_LOUDNESS 1 + +#define MPEG_LAYER_MP1 (1 << 2) +#define MPEG_LAYER_MP2 (1 << 1) +#define MPEG_LAYER_MP3 1 + +#define MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define MPEG_SAMPLING_FREQ_48000 1 #define MAX_BITPOOL 64 #define MIN_BITPOOL 2 @@ -64,6 +75,19 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t channel_mode:4; + uint8_t crc:1; + uint8_t layer:3; + uint8_t frequency:6; + uint8_t mpf:1; + uint8_t rfa:1; + uint8_t bitrate0:7; + uint8_t vbr:1; + uint8_t bitrate1; +} __attribute__ ((packed)); + #elif __BYTE_ORDER == __BIG_ENDIAN struct sbc_codec_cap { @@ -77,6 +101,19 @@ struct sbc_codec_cap { uint8_t max_bitpool; } __attribute__ ((packed)); +struct mpeg_codec_cap { + struct avdtp_media_codec_capability cap; + uint8_t layer:3; + uint8_t crc:1; + uint8_t channel_mode:4; + uint8_t rfa:1; + uint8_t mpf:1; + uint8_t frequency:6; + uint8_t vbr:1; + uint8_t bitrate0:7; + uint8_t bitrate1; +} __attribute__ ((packed)); + #else #error "Unknown byte order" #endif diff --git a/audio/ipc.h b/audio/ipc.h index 3e1200fa..64ed65a7 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -162,8 +162,14 @@ typedef struct { uint8_t max_bitpool; } __attribute__ ((packed)) sbc_capabilities_t; -/* To be defined */ typedef struct { + uint8_t channel_mode; + uint8_t crc; + uint8_t layer; + uint8_t frequency; + uint8_t mpf; + uint8_t vbr; + uint16_t bitrate; } __attribute__ ((packed)) mpeg_capabilities_t; struct bt_getcapabilities_rsp { diff --git a/audio/sink.c b/audio/sink.c index 259abf8f..a95c2e45 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -193,28 +193,28 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { switch (freq) { - case A2DP_SAMPLING_FREQ_16000: - case A2DP_SAMPLING_FREQ_32000: + case SBC_SAMPLING_FREQ_16000: + case SBC_SAMPLING_FREQ_32000: return 53; - case A2DP_SAMPLING_FREQ_44100: + case SBC_SAMPLING_FREQ_44100: switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: return 31; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: return 53; default: error("Invalid channel mode %u", mode); return 53; } - case A2DP_SAMPLING_FREQ_48000: + case SBC_SAMPLING_FREQ_48000: switch (mode) { - case A2DP_CHANNEL_MODE_MONO: - case A2DP_CHANNEL_MODE_DUAL_CHANNEL: + case SBC_CHANNEL_MODE_MONO: + case SBC_CHANNEL_MODE_DUAL_CHANNEL: return 29; - case A2DP_CHANNEL_MODE_STEREO: - case A2DP_CHANNEL_MODE_JOINT_STEREO: + case SBC_CHANNEL_MODE_STEREO: + case SBC_CHANNEL_MODE_JOINT_STEREO: return 51; default: error("Invalid channel mode %u", mode); @@ -236,58 +236,58 @@ static gboolean select_sbc_params(struct sbc_codec_cap *cap, cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; cap->cap.media_codec_type = A2DP_CODEC_SBC; - if (supported->frequency & A2DP_SAMPLING_FREQ_44100) - cap->frequency = A2DP_SAMPLING_FREQ_44100; - else if (supported->frequency & A2DP_SAMPLING_FREQ_48000) - cap->frequency = A2DP_SAMPLING_FREQ_48000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_32000) - cap->frequency = A2DP_SAMPLING_FREQ_32000; - else if (supported->frequency & A2DP_SAMPLING_FREQ_16000) - cap->frequency = A2DP_SAMPLING_FREQ_16000; + if (supported->frequency & SBC_SAMPLING_FREQ_44100) + cap->frequency = SBC_SAMPLING_FREQ_44100; + else if (supported->frequency & SBC_SAMPLING_FREQ_48000) + cap->frequency = SBC_SAMPLING_FREQ_48000; + else if (supported->frequency & SBC_SAMPLING_FREQ_32000) + cap->frequency = SBC_SAMPLING_FREQ_32000; + else if (supported->frequency & SBC_SAMPLING_FREQ_16000) + cap->frequency = SBC_SAMPLING_FREQ_16000; else { error("No supported frequencies"); return FALSE; } - if (supported->channel_mode & A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = A2DP_CHANNEL_MODE_STEREO; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (supported->channel_mode & A2DP_CHANNEL_MODE_MONO) - cap->channel_mode = A2DP_CHANNEL_MODE_MONO; + if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; + else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO) + cap->channel_mode = SBC_CHANNEL_MODE_STEREO; + else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; + else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO) + cap->channel_mode = SBC_CHANNEL_MODE_MONO; else { error("No supported channel modes"); return FALSE; } - if (supported->block_length & A2DP_BLOCK_LENGTH_16) - cap->block_length = A2DP_BLOCK_LENGTH_16; - else if (supported->block_length & A2DP_BLOCK_LENGTH_12) - cap->block_length = A2DP_BLOCK_LENGTH_12; - else if (supported->block_length & A2DP_BLOCK_LENGTH_8) - cap->block_length = A2DP_BLOCK_LENGTH_8; - else if (supported->block_length & A2DP_BLOCK_LENGTH_4) - cap->block_length = A2DP_BLOCK_LENGTH_4; + if (supported->block_length & SBC_BLOCK_LENGTH_16) + cap->block_length = SBC_BLOCK_LENGTH_16; + else if (supported->block_length & SBC_BLOCK_LENGTH_12) + cap->block_length = SBC_BLOCK_LENGTH_12; + else if (supported->block_length & SBC_BLOCK_LENGTH_8) + cap->block_length = SBC_BLOCK_LENGTH_8; + else if (supported->block_length & SBC_BLOCK_LENGTH_4) + cap->block_length = SBC_BLOCK_LENGTH_4; else { error("No supported block lengths"); return FALSE; } - if (supported->subbands & A2DP_SUBBANDS_8) - cap->subbands = A2DP_SUBBANDS_8; - else if (supported->subbands & A2DP_SUBBANDS_4) - cap->subbands = A2DP_SUBBANDS_4; + if (supported->subbands & SBC_SUBBANDS_8) + cap->subbands = SBC_SUBBANDS_8; + else if (supported->subbands & SBC_SUBBANDS_4) + cap->subbands = SBC_SUBBANDS_4; else { error("No supported subbands"); return FALSE; } - if (supported->allocation_method & A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = A2DP_ALLOCATION_LOUDNESS; - else if (supported->allocation_method & A2DP_ALLOCATION_SNR) - cap->allocation_method = A2DP_ALLOCATION_SNR; + if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS) + cap->allocation_method = SBC_ALLOCATION_LOUDNESS; + else if (supported->allocation_method & SBC_ALLOCATION_SNR) + cap->allocation_method = SBC_ALLOCATION_SNR; min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), diff --git a/audio/unix.c b/audio/unix.c index d9f7c670..2226579d 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -341,6 +341,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, struct bt_getcapabilities_rsp *rsp = (void *) buf; struct a2dp_data *a2dp = &client->d.a2dp; struct sbc_codec_cap *sbc_cap = NULL; + struct mpeg_codec_cap *mpeg_cap = NULL; GSList *l; if (err) @@ -364,10 +365,11 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, codec_cap = (void *) cap->data; - if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) { + if (codec_cap->media_codec_type == A2DP_CODEC_SBC && !sbc_cap) sbc_cap = (void *) codec_cap; - break; - } + + if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12 && !mpeg_cap) + mpeg_cap = (void *) codec_cap; } /* endianess prevent direct cast */ @@ -381,6 +383,17 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, rsp->sbc_capabilities.max_bitpool = sbc_cap->max_bitpool; } + if (mpeg_cap) { + rsp->mpeg_capabilities.channel_mode = mpeg_cap->channel_mode; + rsp->mpeg_capabilities.crc = mpeg_cap->crc; + rsp->mpeg_capabilities.layer = mpeg_cap->layer; + rsp->mpeg_capabilities.frequency = mpeg_cap->frequency; + rsp->mpeg_capabilities.mpf = mpeg_cap->mpf; + rsp->mpeg_capabilities.vbr = mpeg_cap->vbr; + rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 & + mpeg_cap->bitrate0 << 8; + } + unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); return; -- cgit From 1cacae6dd9f44d0e403aa29e45eb3d20e7127f68 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 23 Jan 2008 13:12:07 +0000 Subject: Fix HFP notification when ringing is canceled --- audio/headset.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index e655672c..a02f4fef 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1289,6 +1289,16 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, hs->ring_timer = 0; done: + if (hs->hfp_active) { + int err; + /*+CIEV: (callsetup = 0)*/ + err = headset_send(hs, "\r\n+CIEV:3, 0\r\n"); + if (err < 0) { + dbus_message_unref(reply); + return error_failed_errno(conn, msg, -err); + } + } + send_message_and_unref(conn, reply); return DBUS_HANDLER_RESULT_HANDLED; -- cgit From a0af7ee44534dad8f35a4142c6a22177e54ffc57 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:14:02 +0000 Subject: Make a2dpsink to act like a bin and split the payloader. --- audio/Makefile.am | 4 +- audio/gsta2dpsendersink.c | 1013 +++++++++++++++++++++++++++++++++++++++++++ audio/gsta2dpsendersink.h | 90 ++++ audio/gsta2dpsink.c | 1042 +++++++++++---------------------------------- audio/gsta2dpsink.h | 29 +- audio/gstbluetooth.c | 20 +- audio/gstrtpsbcpay.c | 337 +++++++++++++++ audio/gstrtpsbcpay.h | 65 +++ audio/gstsbcdec.c | 17 +- audio/gstsbcdec.h | 2 + audio/gstsbcenc.c | 131 ++---- audio/gstsbcenc.h | 2 + audio/gstsbcparse.c | 223 ++++------ audio/gstsbcparse.h | 5 + audio/gstsbcutil.c | 100 +++++ audio/gstsbcutil.h | 19 + 16 files changed, 2037 insertions(+), 1062 deletions(-) create mode 100644 audio/gsta2dpsendersink.c create mode 100644 audio/gsta2dpsendersink.h create mode 100644 audio/gstrtpsbcpay.c create mode 100644 audio/gstrtpsbcpay.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index f9284b05..5d838c85 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -43,11 +43,13 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ + gsta2dpsendersink.h gsta2dpsendersink.c \ gsta2dpsink.h gsta2dpsink.c \ gstsbcutil.h gstsbcutil.c \ + gstrtpsbcpay.h gstrtpsbcpay.c \ rtp.h ipc.h ipc.c libgstbluetooth_la_LDFLAGS = -module -avoid-version -export-symbols-regex gst_plugin_desc -libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 +libgstbluetooth_la_LIBADD = @SBC_LIBS@ @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10 libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c new file mode 100644 index 00000000..cfb67b87 --- /dev/null +++ b/audio/gsta2dpsendersink.c @@ -0,0 +1,1013 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "ipc.h" +#include "rtp.h" +#include "gstsbcutil.h" + +#include "gsta2dpsendersink.h" + +GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug); +#define GST_CAT_DEFAULT a2dp_sender_sink_debug + +#define BUFFER_SIZE 2048 +#define TEMPLATE_MAX_BITPOOL 64 + +#define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START { \ + g_mutex_lock (s->sink_lock); \ + } G_STMT_END + +#define GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ + g_mutex_unlock (s->sink_lock); \ + } G_STMT_END + + +struct bluetooth_data { + struct bt_getcapabilities_rsp caps; /* Bluetooth device caps */ + guint link_mtu; + + gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ +}; + +#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) +#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) + +enum { + PROP_0, + PROP_DEVICE, +}; + +GST_BOILERPLATE(GstA2dpSenderSink, gst_a2dp_sender_sink, GstBaseSink, + GST_TYPE_BASE_SINK); + +static const GstElementDetails a2dp_sender_sink_details = + GST_ELEMENT_DETAILS("Bluetooth A2DP sink", + "Sink/Audio", + "Plays audio to an A2DP device", + "Marcel Holtmann "); + +static GstStaticPadTemplate a2dp_sender_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"SBC\"" + )); + +static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, + const bt_audio_msg_header_t *msg); +static GIOError gst_a2dp_sender_sink_audioservice_expect( + GstA2dpSenderSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type); + + +static void gst_a2dp_sender_sink_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&a2dp_sender_sink_factory)); + + gst_element_class_set_details(element_class, &a2dp_sender_sink_details); +} + +static gboolean gst_a2dp_sender_sink_stop(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + GST_INFO_OBJECT(self, "stop"); + + if (self->watch_id != 0) { + g_source_remove(self->watch_id); + self->watch_id = 0; + } + + if (self->server) { + bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); + g_io_channel_unref(self->server); + self->server = NULL; + } + + if (self->stream) { + g_io_channel_flush(self->stream, NULL); + g_io_channel_close(self->stream); + g_io_channel_unref(self->stream); + self->stream = NULL; + } + + if (self->data) { + g_free(self->data); + self->data = NULL; + } + + if (self->stream_caps) { + gst_caps_unref(self->stream_caps); + self->stream_caps = NULL; + } + + if (self->dev_caps) { + gst_caps_unref(self->dev_caps); + self->dev_caps = NULL; + } + + return TRUE; +} + +static void gst_a2dp_sender_sink_finalize(GObject *object) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(object); + + if (self->data) + gst_a2dp_sender_sink_stop(GST_BASE_SINK(self)); + + if (self->device) + g_free(self->device); + + g_mutex_free(self->sink_lock); + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void gst_a2dp_sender_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + if (sink->device) + g_free(sink->device); + sink->device = g_value_dup_string(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_a2dp_sender_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string(value, sink->device); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gint gst_a2dp_sender_sink_bluetooth_recvmsg_fd(GstA2dpSenderSink *sink) +{ + int err, ret; + + ret = bt_audio_service_get_data_fd( + g_io_channel_unix_get_fd(sink->server)); + + if (ret < 0) { + err = errno; + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + strerror(err), err); + return -err; + } + + sink->stream = g_io_channel_unix_new(ret); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); + + return 0; +} + +static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink, + GstCaps *caps, + sbc_capabilities_t *pkt) +{ + sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; + const GValue *value = NULL; + const char *pref, *name; + gint rate, subbands, blocks; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + name = gst_structure_get_name(structure); + /* FIXME only sbc supported here, should suport mp3 */ + if (!(IS_SBC(name))) { + GST_ERROR_OBJECT(sink, "Unsupported format %s", name); + return FALSE; + } + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; + else if (rate == 48000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; + else if (rate == 32000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; + else if (rate == 16000) + cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); + return FALSE; + } + + value = gst_structure_get_value(structure, "mode"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else { + GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "allocation"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; + else if (strcmp(pref, "loudness") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; + else { + GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "subbands"); + subbands = g_value_get_int(value); + if (subbands == 8) + cfg->subbands = BT_A2DP_SUBBANDS_8; + else if (subbands == 4) + cfg->subbands = BT_A2DP_SUBBANDS_4; + else { + GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); + return FALSE; + } + + value = gst_structure_get_value(structure, "blocks"); + blocks = g_value_get_int(value); + if (blocks == 16) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (blocks == 12) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (blocks == 8) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (blocks == 4) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); + return FALSE; + } + + value = gst_structure_get_value(structure, "bitpool"); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); + + memcpy(pkt, cfg, sizeof(*pkt)); + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_conf_recv_stream_fd( + GstA2dpSenderSink *self) +{ + struct bluetooth_data *data = self->data; + gint ret; + GIOError err; + GError *gerr = NULL; + GIOStatus status; + GIOFlags flags; + gsize read; + + ret = gst_a2dp_sender_sink_bluetooth_recvmsg_fd(self); + if (ret < 0) + return FALSE; + + if (!self->stream) { + GST_ERROR_OBJECT(self, "Error while configuring device: " + "could not acquire audio socket"); + return FALSE; + } + + /* set stream socket to nonblock */ + GST_LOG_OBJECT(self, "setting stream socket to nonblock"); + flags = g_io_channel_get_flags(self->stream); + flags |= G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to nonblock: " + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to nonblock"); + } + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + GST_LOG_OBJECT(self, "emptying stream pipe"); + while (1) { + err = g_io_channel_read(self->stream, data->buffer, + (gsize) data->link_mtu, + &read); + if (err != G_IO_ERROR_NONE || read <= 0) + break; + } + + /* set stream socket to block */ + GST_LOG_OBJECT(self, "setting stream socket to block"); + flags = g_io_channel_get_flags(self->stream); + flags &= ~G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to block:" + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to block"); + } + + memset(data->buffer, 0, sizeof(data->buffer)); + + return TRUE; +} + +static gboolean server_callback(GIOChannel *chan, + GIOCondition cond, gpointer data) +{ + GstA2dpSenderSink *sink; + + if (cond & G_IO_HUP || cond & G_IO_NVAL) + return FALSE; + else if (cond & G_IO_ERR) { + sink = GST_A2DP_SENDER_SINK(data); + GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); + } + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +{ + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; + GstStructure *structure; + GValue *value; + GValue *list; + gchar *tmp; + gboolean mono, stereo; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_structure_empty_new("audio/x-sbc"); + value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + + /* mode */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "mode", list); + g_free(list); + list = NULL; + } + + /* subbands */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + value = g_value_init(value, G_TYPE_INT); + if (sbc->subbands & BT_A2DP_SUBBANDS_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + if (sbc->subbands & BT_A2DP_SUBBANDS_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "subbands", list); + g_free(list); + list = NULL; + } + + /* blocks */ + value = g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { + g_value_set_int(value, 16); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { + g_value_set_int(value, 12); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "blocks", list); + g_free(list); + list = NULL; + } + + /* allocation */ + g_value_init(value, G_TYPE_STRING); + list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); + if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "allocation", list); + g_free(list); + list = NULL; + } + + /* rate */ + g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* bitpool */ + value = g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, + MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), + MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); + gst_structure_set_value(structure, "bitpool", value); + g_value_unset(value); + + /* channels */ + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + mono = FALSE; + stereo = FALSE; + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + if (self->dev_caps != NULL) + gst_caps_unref(self->dev_caps); + self->dev_caps = gst_caps_new_full(structure, NULL); + + tmp = gst_caps_to_string(self->dev_caps); + GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); + g_free(tmp); + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) +{ + gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *req = (void *) buf; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + GIOError io_error; + + memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + + req->h.msg_type = BT_GETCAPABILITIES_REQ; + if (self->device == NULL) + return FALSE; + strncpy(req->device, self->device, 18); + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while asking device caps"); + } + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_GETCAPABILITIES_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while getting device caps"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + memcpy(&self->data->caps, rsp, sizeof(*rsp)); + if (!gst_a2dp_sender_sink_update_caps(self)) { + GST_WARNING_OBJECT(self, "failed to update capabilities"); + return FALSE; + } + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + gint sk; + gint err; + + GST_INFO_OBJECT(self, "start"); + + self->watch_id = 0; + + sk = bt_audio_service_open(); + if (sk <= 0) { + err = errno; + GST_ERROR_OBJECT(self, "Cannot open connection to bt " + "audio service: %s %d", strerror(err), err); + goto failed; + } + + self->server = g_io_channel_unix_new(sk); + self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | + G_IO_NVAL, server_callback, self); + + self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); + + self->stream = NULL; + self->stream_caps = NULL; + + if (!gst_a2dp_sender_sink_get_capabilities(self)) + goto failed; + + return TRUE; + +failed: + bt_audio_service_close(sk); + return FALSE; +} + +static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *req = (void *) buf; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_streamfd_ind *ind = (void*) buf; + GIOError io_error; + + GST_DEBUG_OBJECT(self, "stream start"); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_STREAMSTART_REQ; + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "start packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream start packet sent"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_STREAMSTART_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while stream " + "start confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream started"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, &ind->h, + BT_STREAMFD_IND); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving " + "stream filedescriptor"); + return FALSE; + } + + if (!gst_a2dp_sender_sink_conf_recv_stream_fd(self)) + return FALSE; + + return TRUE; +} + +static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, + GstCaps *caps) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *req = (void *) buf; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + gboolean ret; + GIOError io_error; + gchar *temp; + + temp = gst_caps_to_string(caps); + GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); + g_free(temp); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_SETCONFIGURATION_REQ; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + strncpy(req->device, self->device, 18); + ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, + &req->sbc_capabilities); + if (!ret) { + GST_ERROR_OBJECT(self, "Couldn't parse caps " + "to packet configuration"); + return FALSE; + } + + io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "configurarion packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "configuration packet sent"); + + io_error = gst_a2dp_sender_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_SETCONFIGURATION_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving device " + "confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : " + "%s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + self->data->link_mtu = rsp->link_mtu; + GST_DEBUG_OBJECT(self, "configuration set"); + + return TRUE; +} + +static GstFlowReturn gst_a2dp_sender_sink_preroll(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(basesink); + gboolean ret; + + GST_A2DP_SENDER_SINK_MUTEX_LOCK(sink); + + ret = gst_a2dp_sender_sink_stream_start(sink); + + GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(sink); + + if (!ret) + return GST_FLOW_ERROR; + + return GST_FLOW_OK; +} + +static GstFlowReturn gst_a2dp_sender_sink_render(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + gsize ret; + GIOError err; + + err = g_io_channel_write(self->stream, (gchar*)GST_BUFFER_DATA(buffer), + (gsize)(GST_BUFFER_SIZE(buffer)), &ret); + + if (err != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while writting to socket: %d %s", + errno, strerror(errno)); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static gboolean gst_a2dp_sender_sink_unlock(GstBaseSink *basesink) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + if (self->stream != NULL) + g_io_channel_flush (self->stream, NULL); + + return TRUE; +} + +static GstFlowReturn gst_a2dp_sender_sink_buffer_alloc(GstBaseSink *basesink, + guint64 offset, guint size, GstCaps* caps, + GstBuffer **buf) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + + *buf = gst_buffer_new_and_alloc(size); + if (!(*buf)) { + GST_ERROR_OBJECT(self, "buffer allocation failed"); + return GST_FLOW_ERROR; + } + + gst_buffer_set_caps(*buf, caps); + + GST_BUFFER_OFFSET(*buf) = offset; + + return GST_FLOW_OK; +} + +static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + object_class->finalize = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_finalize); + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_get_property); + + basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_stop); + basesink_class->render = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_render); + basesink_class->preroll = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_preroll); + basesink_class->unlock = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_unlock); + + basesink_class->buffer_alloc = + GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc); + + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT(a2dp_sender_sink_debug, "a2dpsendersink", 0, + "A2DP sink element"); +} + +static void gst_a2dp_sender_sink_init(GstA2dpSenderSink *self, + GstA2dpSenderSinkClass *klass) +{ + self->device = NULL; + self->data = NULL; + + self->stream = NULL; + + self->dev_caps = NULL; + + self->sink_lock = g_mutex_new(); +} + +static GIOError gst_a2dp_sender_sink_audioservice_send( + GstA2dpSenderSink *self, + const bt_audio_msg_header_t *msg) +{ + GIOError error; + gsize written; + + error = g_io_channel_write(self->server, (const gchar*) msg, + BT_AUDIO_IPC_PACKET_SIZE, &written); + if (error != G_IO_ERROR_NONE) + GST_ERROR_OBJECT(self, "Error sending data to audio service:" + " %s(%d)", strerror(errno), errno); + + return error; +} + +static GIOError gst_a2dp_sender_sink_audioservice_recv( + GstA2dpSenderSink *self, + bt_audio_msg_header_t *inmsg) +{ + GIOError status; + gsize bytes_read; + const char *type; + + status = g_io_channel_read(self->server, (gchar*) inmsg, + BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); + if (status != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error receiving data from " + "audio service"); + return status; + } + + type = bt_audio_strmsg(inmsg->msg_type); + if (!type) { + status = G_IO_ERROR_INVAL; + GST_ERROR_OBJECT(self, "Bogus message type %d " + "received from audio service", + inmsg->msg_type); + } + + return status; +} + +static GIOError gst_a2dp_sender_sink_audioservice_expect( + GstA2dpSenderSink *self, bt_audio_msg_header_t *outmsg, + int expected_type) +{ + GIOError status; + + status = gst_a2dp_sender_sink_audioservice_recv(self, outmsg); + if (status != G_IO_ERROR_NONE) + return status; + + if (outmsg->msg_type != expected_type) + status = G_IO_ERROR_INVAL; + + return status; +} + +gboolean gst_a2dp_sender_sink_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "a2dpsendersink", + GST_RANK_NONE, GST_TYPE_A2DP_SENDER_SINK); +} + + +/* public functions */ +GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink) +{ + if (sink->dev_caps == NULL) + return NULL; + + return gst_caps_copy(sink->dev_caps); +} + +gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *self, + GstCaps *caps) +{ + gboolean ret; + + GST_DEBUG_OBJECT(self, "setting device caps"); + GST_A2DP_SENDER_SINK_MUTEX_LOCK(self); + ret = gst_a2dp_sender_sink_configure(self, caps); + + if (self->stream_caps) + gst_caps_unref(self->stream_caps); + self->stream_caps = gst_caps_ref(caps); + + GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(self); + + return ret; +} + +guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink) +{ + return sink->data->link_mtu; +} + +void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *self, const gchar* dev) +{ + if (self->device != NULL) + g_free(self->device); + + GST_LOG_OBJECT(self, "Setting device: %s", dev); + self->device = g_strdup(dev); +} + +gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self) +{ + return g_strdup(self->device); +} + diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h new file mode 100644 index 00000000..863ef6cb --- /dev/null +++ b/audio/gsta2dpsendersink.h @@ -0,0 +1,90 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_A2DP_SENDER_SINK_H +#define __GST_A2DP_SENDER_SINK_H + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_A2DP_SENDER_SINK \ + (gst_a2dp_sender_sink_get_type()) +#define GST_A2DP_SENDER_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SENDER_SINK,\ + GstA2dpSenderSink)) +#define GST_A2DP_SENDER_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SENDER_SINK,\ + GstA2dpSenderSinkClass)) +#define GST_IS_A2DP_SENDER_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SENDER_SINK)) +#define GST_IS_A2DP_SENDER_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SENDER_SINK)) + +typedef struct _GstA2dpSenderSink GstA2dpSenderSink; +typedef struct _GstA2dpSenderSinkClass GstA2dpSenderSinkClass; + +struct bluetooth_data; + +struct _GstA2dpSenderSink { + GstBaseSink sink; + + gchar *device; + GIOChannel *stream; + + struct bluetooth_data *data; + GIOChannel *server; + + /* stream connection data */ + GstCaps *stream_caps; + + GstCaps *dev_caps; + + GMutex *sink_lock; + + guint watch_id; +}; + +struct _GstA2dpSenderSinkClass { + GstBaseSinkClass parent_class; +}; + +GType gst_a2dp_sender_sink_get_type(void); + +GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink); +gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *sink, + GstCaps *caps); + +guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink); + +void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *sink, + const gchar* device); + +gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink); + +gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_A2DP_SENDER_SINK_H */ diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 5798bc24..d90909c7 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -26,166 +26,67 @@ #endif #include -#include -#include -#include #include -#include - -#include - -#include "ipc.h" -#include "rtp.h" #include "gstsbcutil.h" - #include "gsta2dpsink.h" -GST_DEBUG_CATEGORY_STATIC(a2dp_sink_debug); -#define GST_CAT_DEFAULT a2dp_sink_debug - -#define BUFFER_SIZE 2048 -#define TEMPLATE_MAX_BITPOOL_VALUE 64 +GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug); +#define GST_CAT_DEFAULT gst_a2dp_sink_debug -#define GST_A2DP_SINK_MUTEX_LOCK(s) G_STMT_START { \ - g_mutex_lock (s->sink_lock); \ - } G_STMT_END - -#define GST_A2DP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ - g_mutex_unlock (s->sink_lock); \ - } G_STMT_END - - -struct bluetooth_data { - struct bt_getcapabilities_rsp caps; /* Bluetooth device capabilities */ - struct bt_setconfiguration_rsp cfg; /* Bluetooth device configuration */ - int samples; /* Number of encoded samples */ - gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - gsize count; /* Codec transfer buffer counter */ - - int nsamples; /* Cumulative number of codec samples */ - uint16_t seq_num; /* Cumulative packet sequence */ - int frame_count; /* Current frames in buffer*/ -}; - - -#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) -#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) +#define A2DP_SBC_RTP_PAYLOAD_TYPE 1 +#define TEMPLATE_MAX_BITPOOL_STR "64" enum { PROP_0, - PROP_DEVICE, + PROP_DEVICE }; -GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBaseSink, GST_TYPE_BASE_SINK); +GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN); -static const GstElementDetails a2dp_sink_details = +static const GstElementDetails gst_a2dp_sink_details = GST_ELEMENT_DETAILS("Bluetooth A2DP sink", "Sink/Audio", "Plays audio to an A2DP device", "Marcel Holtmann "); -static GstStaticPadTemplate a2dp_sink_factory = +static GstStaticPadTemplate gst_a2dp_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " + GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "mode = (string) { mono, dual, stereo, joint }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }," - /* FIXME use constant here */ - "bitpool = (int) [ 2, 64 ]; " - "audio/mpeg, " - "mpegversion = (int) 1, " - "layer = (int) [ 1, 3 ], " - "rate = (int) { 16000, 22050, 24000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ]")); - -static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, - const bt_audio_msg_header_t *msg); -static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, - bt_audio_msg_header_t *outmsg, - int expected_type); - + "allocation = (string) { snr, loudness }, " + "bitpool = (int) [ 2, " + TEMPLATE_MAX_BITPOOL_STR " ]; " + )); static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + gst_element_class_set_details(element_class, + &gst_a2dp_sink_details); gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&a2dp_sink_factory)); - - gst_element_class_set_details(element_class, &a2dp_sink_details); -} - -static gboolean gst_a2dp_sink_stop(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - - GST_INFO_OBJECT(self, "stop"); - - if (self->watch_id != 0) { - g_source_remove(self->watch_id); - self->watch_id = 0; - } - - if (self->stream) { - g_io_channel_flush(self->stream, NULL); - g_io_channel_close(self->stream); - g_io_channel_unref(self->stream); - self->stream = NULL; - } - - if (self->server) { - bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); - g_io_channel_unref(self->server); - self->server = NULL; - } - - if (self->data) { - g_free(self->data); - self->data = NULL; - } - - if (self->stream_caps) { - gst_caps_unref(self->stream_caps); - self->stream_caps = NULL; - } - - if (self->dev_caps) { - gst_caps_unref(self->dev_caps); - self->dev_caps = NULL; - } - - return TRUE; -} - -static void gst_a2dp_sink_finalize(GObject *object) -{ - GstA2dpSink *self = GST_A2DP_SINK(object); - - if (self->data) - gst_a2dp_sink_stop(GST_BASE_SINK(self)); - - if (self->device) - g_free(self->device); - - g_mutex_free(self->sink_lock); - - G_OBJECT_CLASS(parent_class)->finalize(object); + gst_static_pad_template_get(&gst_a2dp_sink_factory)); } static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GstA2dpSink *sink = GST_A2DP_SINK(object); + GstA2dpSink *self = GST_A2DP_SINK(object); switch (prop_id) { case PROP_DEVICE: - if (sink->device) - g_free(sink->device); - sink->device = g_value_dup_string(value); + if (self->sink != NULL) + gst_a2dp_sender_sink_set_device(self->sink, + g_value_get_string(value)); + + if (self->device != NULL) + g_free(self->device); + self->device = g_value_dup_string(value); break; default: @@ -197,11 +98,16 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - GstA2dpSink *sink = GST_A2DP_SINK(object); + GstA2dpSink *self = GST_A2DP_SINK(object); + gchar *device; switch (prop_id) { case PROP_DEVICE: - g_value_set_string(value, sink->device); + if (self->sink != NULL) { + device = gst_a2dp_sender_sink_get_device(self->sink); + if (device != NULL) + g_value_take_string(value, device); + } break; default: @@ -210,775 +116,305 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, } } -static gint gst_a2dp_sink_bluetooth_recvmsg_fd(GstA2dpSink *sink) -{ - int err, ret; - - ret = bt_audio_service_get_data_fd(g_io_channel_unix_get_fd(sink->server)); - - if (ret < 0) { - err = errno; - GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", - strerror(err), err); - return -err; - } - - sink->stream = g_io_channel_unix_new(ret); - GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); - - return 0; -} - -static gboolean gst_a2dp_sink_init_pkt_conf(GstA2dpSink *sink, - GstCaps *caps, - sbc_capabilities_t *pkt) +static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, + GstStateChange transition) { - sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; - const GValue *value = NULL; - const char *pref, *name; - gint rate, blocks, subbands; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - name = gst_structure_get_name(structure); - /* FIXME only sbc supported here, should suport mp3 */ - if (!(IS_SBC(name))) { - GST_ERROR_OBJECT(sink, "Unsupported format %s", name); - return FALSE; - } - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; - else if (rate == 48000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; - else if (rate == 32000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; - else if (rate == 16000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); - return FALSE; - } - - value = gst_structure_get_value(structure, "mode"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else { - GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); - return FALSE; - } + GstA2dpSink *self = GST_A2DP_SINK(element); - value = gst_structure_get_value(structure, "allocation"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; - else { - GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); - return FALSE; - } + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + gst_element_set_state(GST_ELEMENT(self->sink), + GST_STATE_READY); + break; - value = gst_structure_get_value(structure, "subbands"); - subbands = g_value_get_int(value); - if (subbands == 8) - cfg->subbands = BT_A2DP_SUBBANDS_8; - else if (subbands == 4) - cfg->subbands = BT_A2DP_SUBBANDS_4; - else { - GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); - return FALSE; - } + case GST_STATE_CHANGE_READY_TO_NULL: + if (self->newseg_event != NULL) { + gst_event_unref(self->newseg_event); + self->newseg_event = NULL; + } + break; - value = gst_structure_get_value(structure, "blocks"); - blocks = g_value_get_int(value); - if (blocks == 16) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (blocks == 12) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (blocks == 8) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (blocks == 4) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); - return FALSE; + default: + break; } - /* FIXME min and max ??? */ - value = gst_structure_get_value(structure, "bitpool"); - - cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - - memcpy(pkt, cfg, sizeof(*pkt)); - - return TRUE; + return GST_ELEMENT_CLASS(parent_class)->change_state(element, + transition); } -static gboolean gst_a2dp_sink_conf_recv_stream_fd(GstA2dpSink *self) +static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) { - struct bluetooth_data *data = self->data; - gint ret; - GIOError err; - GError *gerr = NULL; - GIOStatus status; - GIOFlags flags; - gsize read; - - ret = gst_a2dp_sink_bluetooth_recvmsg_fd(self); - if (ret < 0) - return FALSE; - - if (!self->stream) { - GST_ERROR_OBJECT(self, "Error while configuring device: " - "could not acquire audio socket"); - return FALSE; - } + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - /* set stream socket to nonblock */ - GST_LOG_OBJECT(self, "setting stream socket to nonblock"); - flags = g_io_channel_get_flags(self->stream); - flags |= G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to nonblock: " - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to nonblock"); - } + parent_class = g_type_class_peek_parent(klass); - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - GST_LOG_OBJECT(self, "emptying stream pipe"); - while (1) { - err = g_io_channel_read(self->stream, data->buffer, - (gsize) data->cfg.link_mtu, - &read); - if (err != G_IO_ERROR_NONE || read <= 0) - break; - } + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_get_property); - /* set stream socket to block */ - GST_LOG_OBJECT(self, "setting stream socket to block"); - flags = g_io_channel_get_flags(self->stream); - flags &= ~G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to block:" - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to block"); - } + element_class->change_state = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_change_state); - memset(data->buffer, 0, sizeof(data->buffer)); + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); - return TRUE; + GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0, + "A2DP sink element"); } -static gboolean server_callback(GIOChannel *chan, - GIOCondition cond, gpointer data) +static GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) { - GstA2dpSink *sink; - - if (cond & G_IO_HUP || cond & G_IO_NVAL) - return FALSE; - else if (cond & G_IO_ERR) { - sink = GST_A2DP_SINK(data); - GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - } - - return TRUE; + return gst_a2dp_sender_sink_get_device_caps(self->sink); } -static gboolean gst_a2dp_sink_update_caps(GstA2dpSink *self) +static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad) { - sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; - GstStructure *structure; - GValue *value; - GValue *list; - gchar *tmp; - - GST_LOG_OBJECT(self, "updating device caps"); - - structure = gst_structure_empty_new("audio/x-sbc"); - value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); - - /* mode */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } else { - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "mode", list); - g_free(list); - list = NULL; - } - - /* subbands */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - value = g_value_init(value, G_TYPE_INT); - if (sbc->subbands & BT_A2DP_SUBBANDS_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - if (sbc->subbands & BT_A2DP_SUBBANDS_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "subbands", list); - g_free(list); - list = NULL; - } - - /* blocks */ - value = g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { - g_value_set_int(value, 16); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { - g_value_set_int(value, 12); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "blocks", list); - g_free(list); - list = NULL; - } - - /* allocation */ - g_value_init(value, G_TYPE_STRING); - list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); + GstCaps *caps; + GstCaps *caps_aux; + GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + + if (self->sink == NULL) { + GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized " + "returning template caps"); + caps = gst_static_pad_template_get_caps( + &gst_a2dp_sink_factory); } else { - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "allocation", list); - g_free(list); - list = NULL; - } - - /* rate */ - g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* bitpool */ - value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, - MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL_VALUE), - MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL_VALUE)); - gst_structure_set_value(structure, "bitpool", value); - - /* channels */ - gst_value_set_int_range(value, 1, 2); - gst_structure_set_value(structure, "channels", value); - - g_free(value); - - if (self->dev_caps != NULL) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(structure, NULL); - - tmp = gst_caps_to_string(self->dev_caps); - GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); - g_free(tmp); - - return TRUE; + GST_LOG_OBJECT(self, "Getting device caps"); + caps = gst_a2dp_sink_get_device_caps(self); + if (caps == NULL) + caps = gst_static_pad_template_get_caps( + &gst_a2dp_sink_factory); + } + caps_aux = gst_caps_copy(caps); + g_object_set(self->capsfilter, "caps", caps_aux, NULL); + gst_caps_unref(caps_aux); + return caps; } -static gboolean gst_a2dp_sink_get_capabilities(GstA2dpSink *self) +static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) { - gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_getcapabilities_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_getcapabilities_rsp *rsp = (void *) buf; - GIOError io_error; - - memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + GstElement *sink; - req->h.msg_type = BT_GETCAPABILITIES_REQ; - strncpy(req->device, self->device, 18); - - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while asking device caps"); - } + if (self->sink == NULL) + sink = gst_element_factory_make("a2dpsendersink", "sendersink"); + else + sink = GST_ELEMENT(self->sink); - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_GETCAPABILITIES_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while getting device caps"); + if (sink == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create a2dpsendersink"); return FALSE; } - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; + if (!gst_bin_add(GST_BIN(self), sink)) { + GST_ERROR_OBJECT(self, "failed to add a2dpsendersink " + "to the bin"); + goto cleanup_and_fail; } - memcpy(&self->data->caps, rsp, sizeof(*rsp)); - if (!gst_a2dp_sink_update_caps(self)) { - GST_WARNING_OBJECT(self, "failed to update capabilities"); - return FALSE; + if (gst_element_set_state(sink, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "a2dpsendersink failed to go to ready"); + goto remove_element_and_fail; } - return TRUE; -} - -static gboolean gst_a2dp_sink_start(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - gint sk; - gint err; - - GST_INFO_OBJECT(self, "start"); - - self->watch_id = 0; - - sk = bt_audio_service_open(); - if (sk <= 0) { - err = errno; - GST_ERROR_OBJECT(self, "Cannot open connection to bt " - "audio service: %s %d", strerror(err), err); - goto failed; + if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { + GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " + "to a2dpsendersink"); + goto remove_element_and_fail; } - self->server = g_io_channel_unix_new(sk); - self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | - G_IO_NVAL, server_callback, self); + self->sink = GST_A2DP_SENDER_SINK(sink); + g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); - self->data = g_new0(struct bluetooth_data, 1); - memset(self->data, 0, sizeof(struct bluetooth_data)); + gst_element_set_state(sink, GST_STATE_PAUSED); - self->stream = NULL; - self->stream_caps = NULL; + return TRUE; - if (!gst_a2dp_sink_get_capabilities(self)) - goto failed; +remove_element_and_fail: + gst_element_set_state (sink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), sink); + return FALSE; - return TRUE; +cleanup_and_fail: + if (sink != NULL) + g_object_unref(G_OBJECT(sink)); -failed: - bt_audio_service_close(sk); return FALSE; } -static gboolean gst_a2dp_sink_stream_start(GstA2dpSink *self) +static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) { - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_streamstart_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_streamfd_ind *ind = (void*) buf; - GIOError io_error; - - GST_DEBUG_OBJECT(self, "stream start"); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_STREAMSTART_REQ; - - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "start packet"); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream start packet sent"); - - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_STREAMSTART_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while stream start confirmation"); - return FALSE; - } - - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream started"); + GstElement *rtppay; - io_error = gst_a2dp_sink_audioservice_expect(self, &ind->h, - BT_STREAMFD_IND); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving stream fd"); + rtppay = gst_element_factory_make("rtpsbcpay", "rtp"); + if (rtppay == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay"); return FALSE; } - if (!gst_a2dp_sink_conf_recv_stream_fd(self)) - return FALSE; - - return TRUE; -} - -static gboolean gst_a2dp_sink_configure(GstA2dpSink *self, GstCaps *caps) -{ - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_setconfiguration_req *req = (void *) buf; - bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; - struct bt_setconfiguration_rsp *rsp = (void *) buf; - gboolean ret; - GIOError io_error; - - GST_DEBUG_OBJECT(self, "configuring device"); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_SETCONFIGURATION_REQ; - req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; - strncpy(req->device, self->device, 18); - ret = gst_a2dp_sink_init_pkt_conf(self, caps, &req->sbc_capabilities); - if (!ret) { - GST_ERROR_OBJECT(self, "Couldn't parse caps " - "to packet configuration"); - return FALSE; + if (!gst_bin_add(GST_BIN(self), rtppay)) { + GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin"); + goto cleanup_and_fail; } - io_error = gst_a2dp_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "configurarion packet"); - return FALSE; + if (gst_element_set_state(rtppay, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready"); + goto remove_element_and_fail; } - GST_DEBUG_OBJECT(self, "configuration packet sent"); - - io_error = gst_a2dp_sink_audioservice_expect(self, &rsp_hdr->msg_h, - BT_SETCONFIGURATION_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving device confirmation"); - return FALSE; + if (!gst_element_link(self->capsfilter, rtppay)) { + GST_ERROR_OBJECT(self, "couldn't link capsfilter " + "to rtpsbcpay"); + goto remove_element_and_fail; } - if (rsp_hdr->posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return FALSE; - } + self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); + g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); - memcpy(&self->data->cfg, rsp, sizeof(*rsp)); - GST_DEBUG_OBJECT(self, "configuration set"); + gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; -} -static GstFlowReturn gst_a2dp_sink_preroll(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSink *sink = GST_A2DP_SINK(basesink); - gboolean ret; - - GST_A2DP_SINK_MUTEX_LOCK(sink); - - ret = gst_a2dp_sink_stream_start(sink); - - GST_A2DP_SINK_MUTEX_UNLOCK(sink); +remove_element_and_fail: + gst_element_set_state (rtppay, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), rtppay); + return FALSE; - if (!ret) - return GST_FLOW_ERROR; +cleanup_and_fail: + if (rtppay != NULL) + g_object_unref(G_OBJECT(rtppay)); - return GST_FLOW_OK; + return FALSE; } -static int gst_a2dp_sink_avdtp_write(GstA2dpSink *self) +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) { - gsize ret; - struct bluetooth_data *data = self->data; - struct rtp_header *header; - struct rtp_payload *payload; - GIOError err; - - header = (void *) data->buffer; - payload = (void *) (data->buffer + sizeof(*header)); - - memset(data->buffer, 0, sizeof(*header) + sizeof(*payload)); - - payload->frame_count = data->frame_count; - header->v = 2; - header->pt = 1; - header->sequence_number = htons(data->seq_num); - header->timestamp = htonl(data->nsamples); - header->ssrc = htonl(1); - - err = g_io_channel_write(self->stream, data->buffer, data->count, &ret); - if (err != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while sending data"); - ret = -1; - } - - /* Reset buffer of data to send */ - data->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - data->frame_count = 0; - data->samples = 0; - data->seq_num++; - - return ret; -} + GstA2dpSink *self; + GstStructure *structure; -static GstFlowReturn gst_a2dp_sink_render(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - struct bluetooth_data *data = self->data; - gint encoded; - gint ret; + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + GST_INFO_OBJECT(self, "setting caps"); - encoded = GST_BUFFER_SIZE(buffer); + structure = gst_caps_get_structure(caps, 0); - if (data->count + encoded >= data->cfg.link_mtu) { - ret = gst_a2dp_sink_avdtp_write(self); - if (ret < 0) - return GST_FLOW_ERROR; + /* first, we need to create our rtp payloader */ + if (gst_structure_has_name(structure, "audio/x-sbc")) + gst_a2dp_sink_init_rtp_sbc_element(self); + else { + GST_ERROR_OBJECT(self, "Unexpected media type"); + return FALSE; } - memcpy(data->buffer + data->count, GST_BUFFER_DATA(buffer), encoded); - data->count += encoded; - data->frame_count++; - - return GST_FLOW_OK; -} - -static GstCaps* gst_a2dp_sink_get_caps(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - - if (self->dev_caps) - return gst_caps_ref(self->dev_caps); - - return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(self))); -} - -static gboolean gst_a2dp_sink_set_caps(GstBaseSink *basesink, GstCaps *caps) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); - gboolean ret; - - GST_A2DP_SINK_MUTEX_LOCK(self); - ret = gst_a2dp_sink_configure(self, caps); - - if (self->stream_caps) - gst_caps_unref(self->stream_caps); - self->stream_caps = gst_caps_ref(caps); - - GST_A2DP_SINK_MUTEX_UNLOCK(self); + if (!gst_a2dp_sink_init_sender_sink(self)) + return FALSE; - return ret; -} + if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) + return FALSE; -static gboolean gst_a2dp_sink_unlock(GstBaseSink *basesink) -{ - GstA2dpSink *self = GST_A2DP_SINK(basesink); + g_object_set(G_OBJECT(self->rtp), "mtu", + gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL); - if (self->stream != NULL) - g_io_channel_flush (self->stream, NULL); + /* we forward our new segment here if we have one */ + gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), + self->newseg_event); + self->newseg_event = NULL; - return TRUE; + return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps); } -static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) +/* used for catching newsegment events while we don't have a sink, for + * later forwarding it to the sink */ +static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); + GstA2dpSink *self; - parent_class = g_type_class_peek_parent(klass); + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - object_class->finalize = GST_DEBUG_FUNCPTR(gst_a2dp_sink_finalize); - object_class->set_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_get_property); - - basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sink_start); - basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sink_stop); - basesink_class->render = GST_DEBUG_FUNCPTR(gst_a2dp_sink_render); - basesink_class->preroll = GST_DEBUG_FUNCPTR(gst_a2dp_sink_preroll); - basesink_class->set_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps); - basesink_class->get_caps = GST_DEBUG_FUNCPTR(gst_a2dp_sink_get_caps); - basesink_class->unlock = GST_DEBUG_FUNCPTR(gst_a2dp_sink_unlock); - - g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device address", - NULL, G_PARAM_READWRITE)); + if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT && + gst_element_get_parent(GST_ELEMENT(self->sink)) != + GST_OBJECT_CAST(self)) { + if (self->newseg_event != NULL) + gst_event_unref(self->newseg_event); + self->newseg_event = gst_event_ref(event); + } - GST_DEBUG_CATEGORY_INIT(a2dp_sink_debug, "a2dpsink", 0, - "A2DP sink element"); + return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); } -static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) +static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self) { - self->device = NULL; - self->data = NULL; - - self->stream = NULL; + GstElement *element; - self->dev_caps = NULL; + element = gst_element_factory_make("capsfilter", "filter"); + if (element == NULL) + goto failed; - self->sink_lock = g_mutex_new(); -} + if (!gst_bin_add(GST_BIN(self), element)) + goto failed; -static GIOError gst_a2dp_sink_audioservice_send(GstA2dpSink *self, - const bt_audio_msg_header_t *msg) -{ - gint err; - GIOError error; - gsize written; - - GST_DEBUG_OBJECT(self, "sending %s", bt_audio_strmsg(msg->msg_type)); - - error = g_io_channel_write(self->server, (const gchar*) msg, - BT_AUDIO_IPC_PACKET_SIZE, &written); - if (error != G_IO_ERROR_NONE) { - err = errno; - GST_ERROR_OBJECT(self, "Error sending data to audio service:" - " %s(%d)", strerror(err), err); - } + self->capsfilter = element; + return TRUE; - return error; +failed: + GST_ERROR_OBJECT(self, "Failed to initialize caps filter"); + return FALSE; } -static GIOError gst_a2dp_sink_audioservice_recv(GstA2dpSink *self, - bt_audio_msg_header_t *inmsg) +static void gst_a2dp_sink_init(GstA2dpSink *self, + GstA2dpSinkClass *klass) { - GIOError status; - gsize bytes_read; - const char *type; - - status = g_io_channel_read(self->server, (gchar*) inmsg, - BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); - if (status != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error receiving data from service"); - return status; - } - - type = bt_audio_strmsg(inmsg->msg_type); - if (!type) { - GST_ERROR_OBJECT(self, "Bogus message type %d " - "received from audio service", - inmsg->msg_type); - return G_IO_ERROR_INVAL; - } - - GST_DEBUG_OBJECT(self, "Received %s", type); + GstPad *capsfilter_pad; - return status; + self->sink = NULL; + self->rtp = NULL; + self->device = NULL; + self->capsfilter = NULL; + self->newseg_event = NULL; + + /* we initialize our capsfilter */ + gst_a2dp_sink_init_caps_filter(self); + g_object_set(self->capsfilter, "caps", + gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), + NULL); + + /* we search for the capsfilter sinkpad */ + capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); + + /* now we add a ghostpad */ + self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", + capsfilter_pad)); + g_object_unref(capsfilter_pad); + + /* the getcaps of our ghostpad must reflect the device caps */ + gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_get_caps); + self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); + gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), + GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); + + /* we need to handle events on our own and we also need the eventfunc + * of the ghostpad for forwarding calls */ + self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); + gst_pad_set_event_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_handle_event); + + if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) + GST_ERROR_OBJECT(self, "failed to add ghostpad"); + + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( + "a2dpsendersink", "sendersink")); + if (self->sink == NULL) + GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); } -static GIOError gst_a2dp_sink_audioservice_expect(GstA2dpSink *self, - bt_audio_msg_header_t *outmsg, - int expected_type) +gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin) { - GIOError status; - - status = gst_a2dp_sink_audioservice_recv(self, outmsg); - if (status != G_IO_ERROR_NONE) - return status; - - if (outmsg->msg_type != expected_type) { - GST_ERROR_OBJECT(self, "Bogus message %s " - "received while %s was expected", - bt_audio_strmsg(outmsg->msg_type), - bt_audio_strmsg(expected_type)); - return G_IO_ERROR_INVAL; - } - - return status; + return gst_element_register (plugin, "a2dpsink", + GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index f5b9b69b..4bf9d603 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -22,7 +22,8 @@ */ #include -#include +#include +#include "gsta2dpsendersink.h" G_BEGIN_DECLS @@ -40,31 +41,27 @@ G_BEGIN_DECLS typedef struct _GstA2dpSink GstA2dpSink; typedef struct _GstA2dpSinkClass GstA2dpSinkClass; -struct bluetooth_data; - struct _GstA2dpSink { - GstBaseSink sink; - - gchar *device; - GIOChannel *stream; + GstBin bin; - struct bluetooth_data *data; - GIOChannel *server; + GstBaseRTPPayload *rtp; + GstA2dpSenderSink *sink; + GstElement *capsfilter; - /* stream connection data */ - GstCaps *stream_caps; - - GstCaps *dev_caps; + gchar *device; - GMutex *sink_lock; + GstGhostPad *ghostpad; + GstPadSetCapsFunction ghostpad_setcapsfunc; + GstPadEventFunction ghostpad_eventfunc; - guint watch_id; + GstEvent *newseg_event; }; struct _GstA2dpSinkClass { - GstBaseSinkClass parent_class; + GstBinClass parent_class; }; GType gst_a2dp_sink_get_type(void); +gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin); G_END_DECLS diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 593a311e..764bc899 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -28,7 +28,9 @@ #include "gstsbcenc.h" #include "gstsbcdec.h" #include "gstsbcparse.h" +#include "gsta2dpsendersink.h" #include "gsta2dpsink.h" +#include "gstrtpsbcpay.h" static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); @@ -55,20 +57,22 @@ static gboolean plugin_init(GstPlugin *plugin) SBC_CAPS, NULL, NULL) == FALSE) return FALSE; - if (gst_element_register(plugin, "sbcenc", - GST_RANK_NONE, GST_TYPE_SBC_ENC) == FALSE) + if (!gst_sbc_enc_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "sbcdec", - GST_RANK_PRIMARY, GST_TYPE_SBC_DEC) == FALSE) + if (!gst_sbc_dec_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "sbcparse", - GST_RANK_PRIMARY, GST_TYPE_SBC_PARSE) == FALSE) + if (!gst_sbc_parse_plugin_init(plugin)) return FALSE; - if (gst_element_register(plugin, "a2dpsink", - GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK) == FALSE) + if (!gst_a2dp_sender_sink_plugin_init(plugin)) + return FALSE; + + if (!gst_a2dp_sink_plugin_init(plugin)) + return FALSE; + + if (!gst_rtp_sbc_pay_plugin_init(plugin)) return FALSE; return TRUE; diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c new file mode 100644 index 00000000..68aa28a9 --- /dev/null +++ b/audio/gstrtpsbcpay.c @@ -0,0 +1,337 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "gstrtpsbcpay.h" +#include +#include + +#define RTP_SBC_PAYLOAD_HEADER_SIZE 1 +#define DEFAULT_MIN_FRAMES 0 +#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE) + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_payload { + guint8 frame_count:4; + guint8 rfa0:1; + guint8 is_last_fragment:1; + guint8 is_first_fragment:1; + guint8 is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_payload { + guint8 is_fragmented:1; + guint8 is_first_fragment:1; + guint8 is_last_fragment:1; + guint8 rfa0:1; + guint8 frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +enum { + PROP_0, + PROP_MIN_FRAMES +}; + +GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug + +GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload, + GST_TYPE_BASE_RTP_PAYLOAD); + +static const GstElementDetails gst_rtp_sbc_pay_details = + GST_ELEMENT_DETAILS("RTP packet payloader", + "Codec/Payloader/Network", + "Payload SBC audio as RTP packets", + "Thiago Sousa Santos " + ""); + +static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("audio/x-sbc, " /* FIXME remove those caps? */ + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation = (string) { snr, loudness }," + "bitpool = (int) [ 2, 64 ]; ") + ); + +static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = + GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-rtp") /* FIXME put things here */ + ); + +static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, const gchar* channel_mode) +{ + gint len; + gint join; + + len = 4 + (4 * subbands * channels)/8; + + if (strcmp(channel_mode, "mono") == 0 || + strcmp(channel_mode, "dual") == 0) + len += ((blocks * channels * bitpool)+7) / 8; + else { + join = strcmp(channel_mode, "joint") == 0 ? 1 : 0; + len += ((join * subbands + blocks * bitpool)+7)/8; + } + + return len; +} + +static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload, + GstCaps *caps) +{ + GstRtpSBCPay *sbcpay; + gint rate, subbands, channels, blocks, bitpool; + gint frame_len; + const gchar* channel_mode; + GstStructure *structure; + + sbcpay = GST_RTP_SBC_PAY(payload); + + structure = gst_caps_get_structure(caps, 0); + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int(structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + return FALSE; + if (!gst_structure_get_int(structure, "subbands", &subbands)) + return FALSE; + + channel_mode = gst_structure_get_string(structure, "mode"); + if (!channel_mode) + return FALSE; + + frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks, + bitpool, channel_mode); + + sbcpay->frame_length = frame_len; + + gst_basertppayload_set_options (payload, "audio", FALSE, "SBC", rate); + + GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len); + + return gst_basertppayload_set_outcaps (payload, NULL); +} + +static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) +{ + guint available; + guint max_payload; + GstBuffer* outbuf; + guint8 *payload_data; + guint8 *data; + struct rtp_payload *payload; + + if (sbcpay->frame_length == 0) { + GST_ERROR_OBJECT(sbcpay, "Frame length is 0"); + return GST_FLOW_ERROR; + } + + available = gst_adapter_available(sbcpay->adapter); + + max_payload = gst_rtp_buffer_calc_payload_len( + GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE, + 0, 0); + + max_payload = MIN(max_payload, available); + + outbuf = gst_rtp_buffer_new_allocate(max_payload + + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); + + gst_rtp_buffer_set_payload_type(outbuf, + GST_BASE_RTP_PAYLOAD_PT(sbcpay)); + + data = gst_adapter_take(sbcpay->adapter, max_payload); + payload_data = gst_rtp_buffer_get_payload(outbuf); + + payload = (struct rtp_payload*) payload_data; + memset(payload, 0, sizeof(struct rtp_payload)); + payload->frame_count = max_payload / sbcpay->frame_length; + + memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, max_payload); + g_free(data); + + /* FIXME - timestamp it! */ + GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", max_payload); + + return gst_basertppayload_push (GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); +} + +static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, + GstBuffer *buffer) +{ + GstRtpSBCPay *sbcpay; + guint available; + + sbcpay = GST_RTP_SBC_PAY(payload); + gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); + + available = gst_adapter_available(sbcpay->adapter); + if (available + RTP_SBC_HEADER_TOTAL >= + GST_BASE_RTP_PAYLOAD_MTU(sbcpay) || + (sbcpay->min_frames != -1 && available > + (sbcpay->min_frames * sbcpay->frame_length))) + return gst_rtp_sbc_pay_flush_buffers(sbcpay); + + return GST_FLOW_OK; +} + +static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad, + GstEvent *event) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad)); + + switch (GST_EVENT_TYPE(event)) { + case GST_EVENT_EOS: + gst_rtp_sbc_pay_flush_buffers(sbcpay); + break; + default: + break; + } + + return FALSE; +} + +static void gst_rtp_sbc_pay_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory)); + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory)); + + gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details); +} + +static void gst_rtp_sbc_pay_finalize(GObject *object) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object); + g_object_unref (sbcpay->adapter); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass) +{ + GObjectClass *gobject_class; + GstBaseRTPPayloadClass *payload_class = + GST_BASE_RTP_PAYLOAD_CLASS(klass); + + gobject_class = G_OBJECT_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize); + gobject_class->set_property = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_get_property); + + payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps); + payload_class->handle_buffer = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_handle_buffer); + payload_class->handle_event = GST_DEBUG_FUNCPTR( + gst_rtp_sbc_pay_handle_event); + + /* properties */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_MIN_FRAMES, + g_param_spec_int ("min-frames", "minimum frame number", + "Minimum quantity of frames to send in one packet " + "(-1 for maximum allowed by the mtu)", + -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0, + "RTP SBC payloader"); +} + +static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + sbcpay->min_frames = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + g_value_set_int(value, sbcpay->min_frames); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass) +{ + self->adapter = gst_adapter_new(); + self->frame_length = 0; + + self->min_frames = DEFAULT_MIN_FRAMES; +} + +gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsbcpay", + GST_RANK_NONE, GST_TYPE_RTP_SBC_PAY); +} + diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h new file mode 100644 index 00000000..f086a1c7 --- /dev/null +++ b/audio/gstrtpsbcpay.h @@ -0,0 +1,65 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_SBC_PAY \ + (gst_rtp_sbc_pay_get_type()) +#define GST_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPay)) +#define GST_RTP_SBC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPayClass)) +#define GST_IS_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY)) +#define GST_IS_RTP_SBC_PAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY)) + +typedef struct _GstRtpSBCPay GstRtpSBCPay; +typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass; + +struct _GstRtpSBCPay { + GstBaseRTPPayload base; + + GstAdapter *adapter; + + guint frame_length; + + guint min_frames; +}; + +struct _GstRtpSBCPayClass { + GstBaseRTPPayloadClass parent_class; +}; + +GType gst_rtp_sbc_pay_get_type(void); + +gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index a60c3e69..8c27daba 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -188,10 +188,21 @@ static void gst_sbc_dec_class_init(GstSbcDecClass *klass) static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_dec_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_dec_chain)); + self->sinkpad = gst_pad_new_from_static_template( + &sbc_dec_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR( + sbc_dec_chain)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_dec_src_factory, "src"); + self->srcpad = gst_pad_new_from_static_template( + &sbc_dec_src_factory, "src"); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } + +gboolean gst_sbc_dec_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcdec", + GST_RANK_PRIMARY, GST_TYPE_SBC_DEC); +} + + diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index 4a6922a0..0bb0b57e 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -58,4 +58,6 @@ struct _GstSbcDecClass { GType gst_sbc_dec_get_type(void); +gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 185151f5..08ddc14f 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -120,32 +120,6 @@ static GstStaticPadTemplate sbc_enc_src_factory = gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); -static void sbc_enc_set_structure_int_param(GstSbcEnc *enc, - GstStructure *structure, const gchar* field, - gint field_value) -{ - GValue *value; - - value = g_new0(GValue,1); - value = g_value_init(value, G_TYPE_INT); - g_value_set_int(value, field_value); - gst_structure_set_value(structure, field, value); - g_free(value); -} - -static void sbc_enc_set_structure_string_param(GstSbcEnc *enc, - GstStructure *structure, const gchar* field, - const gchar* field_value) -{ - GValue *value; - - value = g_new0(GValue,1); - value = g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, field_value); - gst_structure_set_value(structure, field, value); - g_free(value); -} - static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) { GstCaps* src_caps; @@ -153,45 +127,49 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) GEnumValue *enum_value; GEnumClass *enum_class; gchar* temp; + GValue *value; src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); structure = gst_caps_get_structure(src_caps, 0); + value = g_new0(GValue, 1); + if (enc->rate != 0) - sbc_enc_set_structure_int_param(enc, structure, "rate", - enc->rate); + gst_sbc_util_set_structure_int_param(structure, "rate", + enc->rate, value); if (enc->channels != 0) - sbc_enc_set_structure_int_param(enc, structure, "channels", - enc->channels); + gst_sbc_util_set_structure_int_param(structure, "channels", + enc->channels, value); if (enc->subbands != 0) - sbc_enc_set_structure_int_param(enc, structure, "subbands", - enc->subbands); + gst_sbc_util_set_structure_int_param(structure, "subbands", + enc->subbands, value); if (enc->blocks != 0) - sbc_enc_set_structure_int_param(enc, structure, "blocks", - enc->blocks); + gst_sbc_util_set_structure_int_param(structure, "blocks", + enc->blocks, value); if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); enum_value = g_enum_get_value(enum_class, enc->mode); - sbc_enc_set_structure_string_param(enc, structure, "mode", - enum_value->value_nick); + gst_sbc_util_set_structure_string_param(structure, "mode", + enum_value->value_nick, value); g_type_class_unref(enum_class); } if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); enum_value = g_enum_get_value(enum_class, enc->allocation); - sbc_enc_set_structure_string_param(enc, structure, "allocation", - enum_value->value_nick); + gst_sbc_util_set_structure_string_param(structure, "allocation", + enum_value->value_nick, value); g_type_class_unref(enum_class); } temp = gst_caps_to_string(src_caps); GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp); g_free(temp); + g_free(value); return src_caps; } @@ -207,23 +185,10 @@ static GstCaps* sbc_enc_src_getcaps (GstPad * pad) static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps) { - GstCaps* srcpad_caps; - GstCaps* temp_caps; - gboolean res = TRUE; GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); GST_LOG_OBJECT(enc, "setting srcpad caps"); - srcpad_caps = sbc_enc_generate_srcpad_caps(enc); - temp_caps = gst_caps_intersect(srcpad_caps, caps); - if (temp_caps == GST_CAPS_NONE) - res = FALSE; - - gst_caps_unref(temp_caps); - gst_caps_unref(srcpad_caps); - - g_return_val_if_fail(res, FALSE); - return gst_sbc_enc_fill_sbc_params(enc, caps); } @@ -313,38 +278,16 @@ error: gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) { - GstStructure *structure; - gint rate, channels, subbands, blocks, bitpool; - const gchar* mode; - const gchar* allocation; - - g_assert(gst_caps_is_fixed(caps)); - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - if (!gst_structure_get_int(structure, "subbands", &subbands)) - return FALSE; - if (!gst_structure_get_int(structure, "blocks", &blocks)) - return FALSE; - if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps)) return FALSE; - if (!(mode = gst_structure_get_string(structure, "mode"))) - return FALSE; - if (!(allocation = gst_structure_get_string(structure, "allocation"))) - return FALSE; - - enc->rate = enc->sbc.rate = rate; - enc->channels = enc->sbc.channels = channels; - enc->blocks = enc->sbc.blocks = blocks; - enc->subbands = enc->sbc.subbands = subbands; - enc->sbc.bitpool = bitpool; - enc->mode = enc->sbc.joint = gst_sbc_get_mode_int(mode); - enc->allocation = enc->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); + enc->rate = enc->sbc.rate; + enc->channels = enc->sbc.channels; + enc->blocks = enc->sbc.blocks; + enc->subbands = enc->sbc.subbands; + enc->mode = enc->sbc.joint; + enc->allocation = enc->sbc.allocation; enc->codesize = sbc_get_codesize(&enc->sbc); enc->frame_length = sbc_get_frame_length(&enc->sbc); enc->frame_duration = sbc_get_frame_duration(&enc->sbc); @@ -390,6 +333,8 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) gst_adapter_flush(adapter, consumed); GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); + /* we have only 1 frame */ + GST_BUFFER_DURATION(output) = enc->frame_duration; res = gst_pad_push(enc->srcpad, output); if (res != GST_FLOW_OK) @@ -559,17 +504,22 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_enc_sink_factory, "sink"); + self->sinkpad = gst_pad_new_from_static_template( + &sbc_enc_sink_factory, "sink"); gst_pad_set_setcaps_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_enc_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_enc_src_factory, "src"); - gst_pad_set_getcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps)); - gst_pad_set_setcaps_function(self->srcpad, GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps)); + self->srcpad = gst_pad_new_from_static_template( + &sbc_enc_src_factory, "src"); + gst_pad_set_getcaps_function(self->srcpad, + GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps)); + gst_pad_set_setcaps_function(self->srcpad, + GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_enc_chain)); + gst_pad_set_chain_function(self->sinkpad, + GST_DEBUG_FUNCPTR(sbc_enc_chain)); self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; self->blocks = SBC_ENC_DEFAULT_BLOCKS; @@ -578,5 +528,16 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) self->rate = SBC_ENC_DEFAULT_RATE; self->channels = SBC_ENC_DEFAULT_CHANNELS; + self->frame_length = 0; + self->frame_duration = 0; + self->adapter = gst_adapter_new(); } + +gboolean gst_sbc_enc_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcenc", + GST_RANK_NONE, GST_TYPE_SBC_ENC); +} + + diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index d81428c9..c7b21638 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -68,4 +68,6 @@ struct _GstSbcEncClass { GType gst_sbc_enc_get_type(void); +gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index bae7d623..49d0bb6e 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -56,171 +56,83 @@ static GstStaticPadTemplate sbc_parse_src_factory = "allocation = (string) { snr, loudness }," "bitpool = (int) [ 2, 64 ]")); -/* Creates a fixed caps from the caps given. */ -/* FIXME use gstsbcutil caps fixating function */ -static GstCaps* sbc_parse_select_caps(GstSbcParse *parse, GstCaps *caps) +static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) { - GstCaps *result; + GstSbcParse *parse; GstStructure *structure; - const GValue *value; - gboolean error = FALSE; - gint temp, rate, channels, blocks, subbands, bitpool; - const gchar* allocation = NULL; - const gchar* mode = NULL; - const gchar* error_message = NULL; - gchar* str; - - str = gst_caps_to_string(caps); - GST_DEBUG_OBJECT(parse, "Parsing caps: %s", str); - g_free(str); + gint rate, channels; + + parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); structure = gst_caps_get_structure(caps, 0); - if (!gst_structure_has_field(structure, "rate")) { - error = TRUE; - error_message = "no rate."; - goto error; - } else { - value = gst_structure_get_value(structure, "rate"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_rate_from_list(value); - else - temp = g_value_get_int(value); - rate = temp; - } + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; - if (!gst_structure_has_field(structure, "channels")) { - error = TRUE; - error_message = "no channels."; - goto error; - } else { - value = gst_structure_get_value(structure, "channels"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) - temp = gst_sbc_select_channels_from_range(value); - else - temp = g_value_get_int(value); - channels = temp; - } + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; - if (!gst_structure_has_field(structure, "blocks")) { - error = TRUE; - error_message = "no blocks."; - goto error; - } else { - value = gst_structure_get_value(structure, "blocks"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_blocks_from_list(value); - else - temp = g_value_get_int(value); - blocks = temp; - } + if (!(parse->rate == 0 || rate == parse->rate)) + return FALSE; - if (!gst_structure_has_field(structure, "subbands")) { - error = TRUE; - error_message = "no subbands."; - goto error; - } else { - value = gst_structure_get_value(structure, "subbands"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_subbands_from_list(value); - else - temp = g_value_get_int(value); - subbands = temp; - } + if (!(parse->channels == 0 || channels == parse->channels)) + return FALSE; - if (!gst_structure_has_field(structure, "bitpool")) { - error = TRUE; - error_message = "no bitpool"; - goto error; - } else { - value = gst_structure_get_value(structure, "bitpool"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) - temp = gst_sbc_select_bitpool_from_range(value); - else - temp = g_value_get_int(value); - bitpool = temp; - } + parse->rate = rate; + parse->channels = channels; - if (!gst_structure_has_field(structure, "allocation")) { - error = TRUE; - error_message = "no allocation."; - goto error; - } else { - value = gst_structure_get_value(structure, "allocation"); - if (GST_VALUE_HOLDS_LIST(value)) - allocation = gst_sbc_get_allocation_from_list(value); - else - allocation = g_value_get_string(value); - } + return gst_sbc_util_fill_sbc_params(&parse->sbc, caps); +} - if (!gst_structure_has_field(structure, "mode")) { - error = TRUE; - error_message = "no mode."; - goto error; - } else { - value = gst_structure_get_value(structure, "mode"); - if (GST_VALUE_HOLDS_LIST(value)) - mode = gst_sbc_get_mode_from_list(value); - else - mode = g_value_get_string(value); - } +static GstCaps* sbc_parse_src_getcaps(GstPad *pad) +{ + GstCaps *caps; + const GstCaps *allowed_caps; + GstStructure *structure; + GValue *value; + GstSbcParse *parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); -error: - if (error) { - GST_ERROR_OBJECT (parse, "Invalid input caps: %s", - error_message); - return NULL; - } + allowed_caps = gst_pad_get_allowed_caps(pad); + if (allowed_caps == NULL) + allowed_caps = gst_pad_get_pad_template_caps(pad); + caps = gst_caps_copy(allowed_caps); - result = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, rate, - "channels", G_TYPE_INT, channels, - "mode", G_TYPE_STRING, mode, - "blocks", G_TYPE_INT, blocks, - "subbands", G_TYPE_INT, subbands, - "allocation", G_TYPE_STRING, allocation, - "bitpool", G_TYPE_INT, bitpool, - NULL); - parse->sbc.rate = rate; - parse->sbc.channels = channels; - parse->sbc.blocks = blocks; - parse->sbc.subbands = subbands; - parse->sbc.bitpool = bitpool; - parse->sbc.joint = gst_sbc_get_mode_int(mode); - parse->sbc.allocation = gst_sbc_get_allocation_mode_int(allocation); - - return result; + value = g_new0(GValue, 1); + + structure = gst_caps_get_structure(caps, 0); + + if (parse->rate != 0) + gst_sbc_util_set_structure_int_param(structure, "rate", + parse->rate, value); + if (parse->channels != 0) + gst_sbc_util_set_structure_int_param(structure, "channels", + parse->channels, value); + + g_free(value); + + return caps; } -static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) +static gboolean sbc_parse_src_acceptcaps(GstPad *pad, GstCaps *caps) { + GstStructure *structure; GstSbcParse *parse; - GstCaps *inter, *other, *srccaps; + gint rate, channels; parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); - other = gst_pad_peer_get_caps(parse->srcpad); - if (other == NULL) - other = gst_caps_new_any(); + structure = gst_caps_get_structure(caps, 0); - inter = gst_caps_intersect(caps, other); - if (gst_caps_is_empty(inter)) { - gst_caps_unref(inter); + if (!gst_structure_get_int(structure, "rate", &rate)) return FALSE; - } - srccaps = sbc_parse_select_caps(parse, inter); - if (srccaps == NULL) { - gst_caps_unref(inter); + if (!gst_structure_get_int(structure, "channels", &channels)) return FALSE; - } - gst_pad_set_caps(parse->srcpad, srccaps); + if ((parse->rate == 0 || parse->rate == rate) + && (parse->channels == 0 || parse->channels == channels)) + return TRUE; - gst_caps_unref(inter); - gst_caps_unref(other); - gst_caps_unref(srccaps); - - return TRUE; + return FALSE; } static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) @@ -234,11 +146,13 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) timestamp = GST_BUFFER_TIMESTAMP(buffer); if (parse->buffer) { - GstBuffer *temp = buffer; + GstBuffer *temp; + temp = buffer; buffer = gst_buffer_span(parse->buffer, 0, buffer, - GST_BUFFER_SIZE(parse->buffer) + GST_BUFFER_SIZE(buffer)); - gst_buffer_unref(temp); + GST_BUFFER_SIZE(parse->buffer) + + GST_BUFFER_SIZE(buffer)); gst_buffer_unref(parse->buffer); + gst_buffer_unref(temp); parse->buffer = NULL; } @@ -300,11 +214,13 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG("Finish subband codec"); + if (parse->buffer) { gst_buffer_unref(parse->buffer); parse->buffer = NULL; } sbc_finish(&parse->sbc); + break; default: @@ -341,12 +257,27 @@ static void gst_sbc_parse_class_init(GstSbcParseClass *klass) static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) { - self->sinkpad = gst_pad_new_from_static_template(&sbc_parse_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain)); + self->sinkpad = gst_pad_new_from_static_template( + &sbc_parse_sink_factory, "sink"); + gst_pad_set_chain_function(self->sinkpad, + GST_DEBUG_FUNCPTR(sbc_parse_chain)); gst_pad_set_setcaps_function (self->sinkpad, GST_DEBUG_FUNCPTR (sbc_parse_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - self->srcpad = gst_pad_new_from_static_template(&sbc_parse_src_factory, "src"); + self->srcpad = gst_pad_new_from_static_template( + &sbc_parse_src_factory, "src"); + gst_pad_set_getcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (sbc_parse_src_getcaps)); + gst_pad_set_acceptcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (sbc_parse_src_acceptcaps)); + /* FIXME get encoding parameters on set caps */ gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } + +gboolean gst_sbc_parse_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "sbcparse", + GST_RANK_NONE, GST_TYPE_SBC_PARSE); +} + diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h index ceaf2197..eb9ca441 100644 --- a/audio/gstsbcparse.h +++ b/audio/gstsbcparse.h @@ -50,6 +50,9 @@ struct _GstSbcParse { GstBuffer *buffer; sbc_t sbc; + + gint channels; + gint rate; }; struct _GstSbcParseClass { @@ -58,4 +61,6 @@ struct _GstSbcParseClass { GType gst_sbc_parse_get_type(void); +gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin); + G_END_DECLS diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index f2351e6b..d791a8d6 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -26,6 +26,7 @@ #endif #include "ipc.h" +#include #include "gstsbcutil.h" /* @@ -306,4 +307,103 @@ error: return result; } +/** + * Sets the int field_value to the param "field" on the structure. + * value is used to do the operation, it must be a uninitialized (zero-filled) + * GValue, it will be left unitialized at the end of the function. + */ +void gst_sbc_util_set_structure_int_param(GstStructure *structure, + const gchar* field, gint field_value, + GValue *value) +{ + value = g_value_init(value, G_TYPE_INT); + g_value_set_int(value, field_value); + gst_structure_set_value(structure, field, value); + g_value_unset(value); +} + +/** + * Sets the string field_value to the param "field" on the structure. + * value is used to do the operation, it must be a uninitialized (zero-filled) + * GValue, it will be left unitialized at the end of the function. + */ +void gst_sbc_util_set_structure_string_param(GstStructure *structure, + const gchar* field, const gchar* field_value, + GValue *value) +{ + value = g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, field_value); + gst_structure_set_value(structure, field, value); + g_value_unset(value); +} + +gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps) +{ + GstStructure *structure; + gint rate, channels, subbands, blocks, bitpool; + const gchar* mode; + const gchar* allocation; + + g_assert(gst_caps_is_fixed(caps)); + + structure = gst_caps_get_structure(caps, 0); + + if (!gst_structure_get_int(structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int(structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int(structure, "subbands", &subbands)) + return FALSE; + if (!gst_structure_get_int(structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int(structure, "bitpool", &bitpool)) + return FALSE; + + if (!(mode = gst_structure_get_string(structure, "mode"))) + return FALSE; + if (!(allocation = gst_structure_get_string(structure, "allocation"))) + return FALSE; + + sbc->rate = rate; + sbc->channels = channels; + sbc->blocks = blocks; + sbc->subbands = subbands; + sbc->bitpool = bitpool; + sbc->joint = gst_sbc_get_mode_int(mode); + sbc->allocation = gst_sbc_get_allocation_mode_int(allocation); + + return TRUE; +} + +gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, gint channel_mode) +{ + gint len; + gint join; + len = 4 + (4 * subbands * channels)/8; + + if (channel_mode == BT_A2DP_CHANNEL_MODE_MONO || + channel_mode == BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) + len += ((blocks * channels * bitpool)+7) / 8; + else { + join = channel_mode == BT_A2DP_CHANNEL_MODE_JOINT_STEREO + ? 1 : 0; + len += ((join * subbands + blocks * bitpool)+7)/8; + } + + return len; +} + +gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, + gint blocks) +{ + return (((frame_len * 8 * rate / subbands) / blocks) / 1000); +} + +gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands) +{ + gint64 res = 1000000; + return res * blocks * subbands / rate; +} + diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 4581abf7..a67bf1be 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -49,3 +49,22 @@ const gchar *gst_sbc_get_mode_string(int joint); GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); + +void gst_sbc_util_set_structure_int_param(GstStructure *structure, + const gchar* field, gint field_value, + GValue *value); + +void gst_sbc_util_set_structure_string_param(GstStructure *structure, + const gchar* field, const gchar* field_value, + GValue *value); + +gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps); + +gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, + gint blocks, gint bitpool, gint channel_mode); + +gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, + gint blocks); + +gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands); + -- cgit From 489c2e0dbd76d9cecec62d19fafd9e7baddb72e7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:16:02 +0000 Subject: Add mpeg12 sep registration. --- audio/a2dp.c | 144 +++++++++++++++++++++++++++++++++++++++++----- audio/a2dp.h | 13 +++-- audio/avdtp.c | 2 + audio/avdtp.h | 1 + audio/gsta2dpsendersink.c | 37 +++++++----- audio/ipc.h | 20 +++++-- audio/pcm_bluetooth.c | 39 +++++++------ audio/unix.c | 62 ++++++++++++++------ 8 files changed, 247 insertions(+), 71 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 1c59374f..b1409f5c 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -58,6 +58,7 @@ struct a2dp_sep { uint8_t type; + uint8_t codec; struct avdtp_local_sep *sep; struct avdtp *session; struct avdtp_stream *stream; @@ -280,7 +281,7 @@ static void stream_state_changed(struct avdtp_stream *stream, } -static gboolean setconf_ind(struct avdtp *session, +static gboolean sbc_setconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, GSList *caps, uint8_t *err, @@ -331,7 +332,7 @@ static gboolean setconf_ind(struct avdtp *session, return TRUE; } -static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, +static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; @@ -386,6 +387,84 @@ static gboolean getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } +static gboolean mpeg_setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + GSList *caps, uint8_t *err, + uint8_t *category, void *user_data) +{ + struct a2dp_sep *a2dp_sep = user_data; + struct device *dev; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) + debug("SBC Sink: Set_Configuration_Ind"); + else + debug("SBC Source: Set_Configuration_Ind"); + + dev = a2dp_get_dev(session); + if (!dev) { + *err = AVDTP_UNSUPPORTED_CONFIGURATION; + *category = 0x00; + return FALSE; + } + + avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); + a2dp_sep->stream = stream; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) + sink_new_stream(dev, session, stream); + + return TRUE; +} + +static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, void *user_data) +{ + struct a2dp_sep *a2dp_sep = user_data; + struct avdtp_service_capability *media_transport, *media_codec; + struct mpeg_codec_cap mpeg_cap; + + if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) + debug("SBC Sink: Get_Capability_Ind"); + else + debug("SBC Source: Get_Capability_Ind"); + + *caps = NULL; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap)); + + mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; + + mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 | + MPEG_SAMPLING_FREQ_44100 | + MPEG_SAMPLING_FREQ_32000 | + MPEG_SAMPLING_FREQ_24000 | + MPEG_SAMPLING_FREQ_22050 | + MPEG_SAMPLING_FREQ_16000 ); + + mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO | + MPEG_CHANNEL_MODE_STEREO | + MPEG_CHANNEL_MODE_DUAL_CHANNEL | + MPEG_CHANNEL_MODE_MONO ); + + mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 ); + + mpeg_cap.bitrate = 0xFFFF; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, + sizeof(mpeg_cap)); + + *caps = g_slist_append(*caps, media_codec); + + return TRUE; +} + static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) @@ -797,9 +876,21 @@ static struct avdtp_sep_cfm cfm = { .reconfigure = reconf_cfm }; -static struct avdtp_sep_ind ind = { - .get_capability = getcap_ind, - .set_configuration = setconf_ind, +static struct avdtp_sep_ind sbc_ind = { + .get_capability = sbc_getcap_ind, + .set_configuration = sbc_setconf_ind, + .get_configuration = getconf_ind, + .open = open_ind, + .start = start_ind, + .suspend = suspend_ind, + .close = close_ind, + .abort = abort_ind, + .reconfigure = reconf_ind +}; + +static struct avdtp_sep_ind mpeg_ind = { + .get_capability = mpeg_getcap_ind, + .set_configuration = mpeg_setconf_ind, .get_configuration = getconf_ind, .open = open_ind, .start = start_ind, @@ -880,23 +971,27 @@ static int a2dp_sink_record(sdp_buf_t *buf) return 0; } -static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type) +static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, + uint8_t codec) { struct a2dp_sep *sep; GSList **l; int (*create_record)(sdp_buf_t *buf); uint32_t *record_id; sdp_buf_t buf; + struct avdtp_sep_ind *ind; sep = g_new0(struct a2dp_sep, 1); - sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, - &ind, &cfm, sep); + ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind; + sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, codec, + ind, &cfm, sep); if (sep->sep == NULL) { g_free(sep); return NULL; } + sep->codec = codec; sep->type = type; if (type == AVDTP_SEP_TYPE_SOURCE) { @@ -945,11 +1040,15 @@ int a2dp_init(DBusConnection *conn, int sources, int sinks) avdtp_init(); - for (i = 0; i < sources; i++) - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); + for (i = 0; i < sources; i++) { + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_MPEG12); + } - for (i = 0; i < sinks; i++) - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); + for (i = 0; i < sinks; i++) { + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_SBC); + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_MPEG12); + } return 0; } @@ -1025,14 +1124,32 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, struct a2dp_sep *sep = NULL; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap = NULL; int posix_err; + for (l = caps; l != NULL; l = l->next) { + cap = l->data; + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec_cap = (void *) cap->data; + break; + } + + if (!codec_cap) + return 0; + for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; if (tmp->locked) continue; + if (tmp->codec != codec_cap->media_codec_type) + continue; + if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) { sep = tmp; break; @@ -1067,7 +1184,8 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, - AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + codec_cap->media_type, + codec_cap->media_codec_type, &lsep, &rsep) < 0) { error("No matching ACP and INT SEPs found"); goto failed; diff --git a/audio/a2dp.h b/audio/a2dp.h index cfd1c47d..84301bad 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -48,6 +48,11 @@ #define SBC_ALLOCATION_SNR (1 << 1) #define SBC_ALLOCATION_LOUDNESS 1 +#define MPEG_CHANNEL_MODE_MONO (1 << 3) +#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) +#define MPEG_CHANNEL_MODE_STEREO (1 << 1) +#define MPEG_CHANNEL_MODE_JOINT_STEREO 1 + #define MPEG_LAYER_MP1 (1 << 2) #define MPEG_LAYER_MP2 (1 << 1) #define MPEG_LAYER_MP3 1 @@ -83,9 +88,7 @@ struct mpeg_codec_cap { uint8_t frequency:6; uint8_t mpf:1; uint8_t rfa:1; - uint8_t bitrate0:7; - uint8_t vbr:1; - uint8_t bitrate1; + uint16_t bitrate; } __attribute__ ((packed)); #elif __BYTE_ORDER == __BIG_ENDIAN @@ -109,9 +112,7 @@ struct mpeg_codec_cap { uint8_t rfa:1; uint8_t mpf:1; uint8_t frequency:6; - uint8_t vbr:1; - uint8_t bitrate0:7; - uint8_t bitrate1; + uint16_t bitrate; } __attribute__ ((packed)); #else diff --git a/audio/avdtp.c b/audio/avdtp.c index d5433452..97868a07 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2696,6 +2696,7 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) } struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data) @@ -2711,6 +2712,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->info.seid = free_seid++; sep->info.type = type; sep->info.media_type = media_type; + sep->codec = codec_type; sep->ind = ind; sep->cfm = cfm; sep->user_data = user_data; diff --git a/audio/avdtp.h b/audio/avdtp.h index 241fa713..01ca50c6 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -242,6 +242,7 @@ int avdtp_close(struct avdtp *session, struct avdtp_stream *stream); int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, struct avdtp_sep_ind *ind, struct avdtp_sep_cfm *cfm, void *user_data); diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c index cfb67b87..1f54f29c 100644 --- a/audio/gsta2dpsendersink.c +++ b/audio/gsta2dpsendersink.c @@ -236,13 +236,13 @@ static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink, value = gst_structure_get_value(structure, "rate"); rate = g_value_get_int(value); if (rate == 44100) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_44100; + cfg->frequency = BT_SBC_SAMPLING_FREQ_44100; else if (rate == 48000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_48000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_48000; else if (rate == 32000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_32000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_32000; else if (rate == 16000) - cfg->frequency = BT_A2DP_SAMPLING_FREQ_16000; + cfg->frequency = BT_SBC_SAMPLING_FREQ_16000; else { GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); return FALSE; @@ -396,17 +396,14 @@ static gboolean server_callback(GIOChannel *chan, return TRUE; } -static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps( + GstA2dpSenderSink *self, sbc_capabilities_t *sbc) { - sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; GstStructure *structure; GValue *value; GValue *list; - gchar *tmp; gboolean mono, stereo; - GST_LOG_OBJECT(self, "updating device caps"); - structure = gst_structure_empty_new("audio/x-sbc"); value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); @@ -518,19 +515,19 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) /* rate */ g_value_init(value, G_TYPE_INT); list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_48000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { g_value_set_int(value, 48000); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_44100) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { g_value_set_int(value, 44100); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_32000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { g_value_set_int(value, 32000); gst_value_list_prepend_value(list, value); } - if (sbc->frequency & BT_A2DP_SAMPLING_FREQ_16000) { + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { g_value_set_int(value, 16000); gst_value_list_prepend_value(list, value); } @@ -584,6 +581,20 @@ static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) gst_structure_set_value(structure, "channels", value); g_free(value); + return structure; +} + +static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) +{ + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; + mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; + GstStructure *structure; + gchar *tmp; + + GST_LOG_OBJECT(self, "updating device caps"); + + structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + if (self->dev_caps != NULL) gst_caps_unref(self->dev_caps); self->dev_caps = gst_caps_new_full(structure, NULL); diff --git a/audio/ipc.h b/audio/ipc.h index 64ed65a7..3768dcfb 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -129,10 +129,10 @@ struct bt_getcapabilities_req { * SBC Codec parameters as per A2DP profile 1.0 § 4.3 */ -#define BT_A2DP_SAMPLING_FREQ_16000 (1 << 3) -#define BT_A2DP_SAMPLING_FREQ_32000 (1 << 2) -#define BT_A2DP_SAMPLING_FREQ_44100 (1 << 1) -#define BT_A2DP_SAMPLING_FREQ_48000 1 +#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3) +#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2) +#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1) +#define BT_SBC_SAMPLING_FREQ_48000 1 #define BT_A2DP_CHANNEL_MODE_MONO (1 << 3) #define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) @@ -152,6 +152,17 @@ struct bt_getcapabilities_req { #define BT_A2DP_ALLOCATION_LOUDNESS 1 #define BT_A2DP_ALLOCATION_AUTO 0 +#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5) +#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4) +#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3) +#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2) +#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1) +#define BT_MPEG_SAMPLING_FREQ_48000 1 + +#define BT_MPEG_LAYER_1 (1 << 2) +#define BT_MPEG_LAYER_2 (1 << 1) +#define BT_MPEG_LAYER_3 1 + typedef struct { uint8_t channel_mode; uint8_t frequency; @@ -168,7 +179,6 @@ typedef struct { uint8_t layer; uint8_t frequency; uint8_t mpf; - uint8_t vbr; uint16_t bitrate; } __attribute__ ((packed)) mpeg_capabilities_t; diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index e81bbc16..a04f18c0 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -468,10 +468,10 @@ static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, static uint8_t default_bitpool(uint8_t freq, uint8_t mode) { switch (freq) { - case BT_A2DP_SAMPLING_FREQ_16000: - case BT_A2DP_SAMPLING_FREQ_32000: + case BT_SBC_SAMPLING_FREQ_16000: + case BT_SBC_SAMPLING_FREQ_32000: return 53; - case BT_A2DP_SAMPLING_FREQ_44100: + case BT_SBC_SAMPLING_FREQ_44100: switch (mode) { case BT_A2DP_CHANNEL_MODE_MONO: case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: @@ -483,7 +483,7 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) DBG("Invalid channel mode %u", mode); return 53; } - case BT_A2DP_SAMPLING_FREQ_48000: + case BT_SBC_SAMPLING_FREQ_48000: switch (mode) { case BT_A2DP_CHANNEL_MODE_MONO: case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: @@ -508,16 +508,16 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, switch (rate) { case 48000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_48000; + cap->frequency = BT_SBC_SAMPLING_FREQ_48000; break; case 44100: - cap->frequency = BT_A2DP_SAMPLING_FREQ_44100; + cap->frequency = BT_SBC_SAMPLING_FREQ_44100; break; case 32000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_32000; + cap->frequency = BT_SBC_SAMPLING_FREQ_32000; break; case 16000: - cap->frequency = BT_A2DP_SAMPLING_FREQ_16000; + cap->frequency = BT_SBC_SAMPLING_FREQ_16000; break; default: DBG("Rate %d not supported", rate); @@ -641,16 +641,16 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, sbc_init(&a2dp->sbc, 0); a2dp->sbc_initialized = 1; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000) a2dp->sbc.rate = 16000; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000) a2dp->sbc.rate = 32000; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100) a2dp->sbc.rate = 44100; - if (active_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) + if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000) a2dp->sbc.rate = 48000; if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) @@ -659,7 +659,8 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, a2dp->sbc.channels = 2; if (active_capabilities.channel_mode & - (BT_A2DP_CHANNEL_MODE_MONO || BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + (BT_A2DP_CHANNEL_MODE_MONO || + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) a2dp->sbc.joint = 1; else a2dp->sbc.joint = 0; @@ -1231,22 +1232,26 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) /* supported rates */ rate_count = 0; - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_16000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_16000) { rate_list[rate_count] = 16000; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_32000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_32000) { rate_list[rate_count] = 32000; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_44100) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_44100) { rate_list[rate_count] = 44100; rate_count++; } - if (a2dp->sbc_capabilities.frequency & BT_A2DP_SAMPLING_FREQ_48000) { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_48000) { rate_list[rate_count] = 48000; rate_count++; } diff --git a/audio/unix.c b/audio/unix.c index 2226579d..d71c420a 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -389,9 +389,7 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, rsp->mpeg_capabilities.layer = mpeg_cap->layer; rsp->mpeg_capabilities.frequency = mpeg_cap->frequency; rsp->mpeg_capabilities.mpf = mpeg_cap->mpf; - rsp->mpeg_capabilities.vbr = mpeg_cap->vbr; - rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate1 & - mpeg_cap->bitrate0 << 8; + rsp->mpeg_capabilities.bitrate = mpeg_cap->bitrate; } unix_ipc_sendmsg(client, &rsp->rsp_h.msg_h); @@ -794,6 +792,7 @@ static void handle_setconfiguration_req(struct unix_client *client, { struct avdtp_service_capability *media_transport, *media_codec; struct sbc_codec_cap sbc_cap; + struct mpeg_codec_cap mpeg_cap; struct device *dev; bdaddr_t bdaddr; int err = 0; @@ -842,20 +841,49 @@ static void handle_setconfiguration_req(struct unix_client *client, client->caps = g_slist_append(client->caps, media_transport); - memset(&sbc_cap, 0, sizeof(sbc_cap)); - - sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; - sbc_cap.frequency = req->sbc_capabilities.frequency; - sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; - sbc_cap.subbands = req->sbc_capabilities.subbands; - sbc_cap.block_length = req->sbc_capabilities.block_length ; - sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; - sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); + if (req->mpeg_capabilities.frequency) { + memset(&mpeg_cap, 0, sizeof(mpeg_cap)); + + mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; + mpeg_cap.channel_mode = req->mpeg_capabilities.channel_mode; + mpeg_cap.crc = req->mpeg_capabilities.crc; + mpeg_cap.layer = req->mpeg_capabilities.layer; + mpeg_cap.frequency = req->mpeg_capabilities.frequency; + mpeg_cap.mpf = req->mpeg_capabilities.mpf; + mpeg_cap.bitrate = req->mpeg_capabilities.bitrate; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, + sizeof(mpeg_cap)); + + info("config mpeg - frequency = %u channel_mode = %u " + "layer = %u crc = %u mpf = %u bitrate = %u", + mpeg_cap.frequency, mpeg_cap.channel_mode, + mpeg_cap.layer, mpeg_cap.crc, mpeg_cap.mpf, + mpeg_cap.bitrate); + } else { + memset(&sbc_cap, 0, sizeof(sbc_cap)); + + sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; + sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; + sbc_cap.channel_mode = req->sbc_capabilities.channel_mode; + sbc_cap.frequency = req->sbc_capabilities.frequency; + sbc_cap.allocation_method = req->sbc_capabilities.allocation_method; + sbc_cap.subbands = req->sbc_capabilities.subbands; + sbc_cap.block_length = req->sbc_capabilities.block_length; + sbc_cap.min_bitpool = req->sbc_capabilities.min_bitpool; + sbc_cap.max_bitpool = req->sbc_capabilities.max_bitpool; + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, + sizeof(sbc_cap)); + + info("config sbc - frequency = %u channel_mode = %u " + "allocation = %u subbands = %u blocks = %u " + "bitpool = %u", sbc_cap.frequency, + sbc_cap.channel_mode, sbc_cap.allocation_method, + sbc_cap.subbands, sbc_cap.block_length, + sbc_cap.max_bitpool); + } client->caps = g_slist_append(client->caps, media_codec); -- cgit From de8b4e7ab91bad2c1c6e796f53d4e69ecbadd0bd Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:19:32 +0000 Subject: Add mp3 support for gstreamer plugin. --- audio/gsta2dpsendersink.c | 345 +++++++++++++++++++++++++++++++++++++++++++++- audio/gsta2dpsendersink.h | 11 ++ audio/gsta2dpsink.c | 212 +++++++++++++++++++++++----- audio/gsta2dpsink.h | 4 + audio/gstbluetooth.c | 23 +++- audio/gstsbcutil.c | 73 +++++++++- audio/gstsbcutil.h | 1 + 7 files changed, 620 insertions(+), 49 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c index 1f54f29c..0256bb93 100644 --- a/audio/gsta2dpsendersink.c +++ b/audio/gsta2dpsendersink.c @@ -35,6 +35,8 @@ #include +#include + #include "ipc.h" #include "rtp.h" #include "gstsbcutil.h" @@ -46,6 +48,8 @@ GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug); #define BUFFER_SIZE 2048 #define TEMPLATE_MAX_BITPOOL 64 +#define CRC_PROTECTED 1 +#define CRC_UNPROTECTED 0 #define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START { \ g_mutex_lock (s->sink_lock); \ @@ -84,7 +88,18 @@ static GstStaticPadTemplate a2dp_sender_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("application/x-rtp, " "media = (string) \"audio\", " - "encoding-name = (string) \"SBC\"" + "encoding-name = (string) \"SBC\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " + GST_RTP_PAYLOAD_MPA_STRING ", " + "clock-rate = (int) 90000; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " + GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"MPA\"" )); static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, @@ -584,20 +599,145 @@ static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps( return structure; } +static GstStructure *gst_a2dp_sender_sink_parse_mpeg_caps( + GstA2dpSenderSink *self, mpeg_capabilities_t *mpeg) +{ + GstStructure *structure; + GValue *value; + GValue *list; + gboolean valid_layer = FALSE; + gboolean mono, stereo; + + GST_LOG_OBJECT(self, "parsing mpeg caps"); + + structure = gst_structure_empty_new("audio/mpeg"); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_INT); + + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + gst_structure_set_value(structure, "mpegversion", list); + g_free(list); + + /* layer */ + GST_LOG_OBJECT(self, "setting mpeg layer"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->layer & BT_MPEG_LAYER_1) { + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_2) { + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_3) { + g_value_set_int(value, 3); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (list) { + gst_structure_set_value(structure, "layer", list); + g_free(list); + list = NULL; + } + + if (!valid_layer) { + gst_structure_free(structure); + g_free(value); + return NULL; + } + + /* rate */ + GST_LOG_OBJECT(self, "setting mpeg rate"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { + g_value_set_int(value, 24000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { + g_value_set_int(value, 22050); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* channels */ + GST_LOG_OBJECT(self, "setting mpeg channels"); + mono = FALSE; + stereo = FALSE; + if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + return structure; +} + static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) { sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; - GstStructure *structure; + GstStructure *sbc_structure; + GstStructure *mpeg_structure; gchar *tmp; GST_LOG_OBJECT(self, "updating device caps"); - structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + sbc_structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); + mpeg_structure = gst_a2dp_sender_sink_parse_mpeg_caps(self, mpeg); if (self->dev_caps != NULL) gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(structure, NULL); + self->dev_caps = gst_caps_new_full(sbc_structure, NULL); + if (mpeg_structure != NULL) + gst_caps_append_structure(self->dev_caps, mpeg_structure); tmp = gst_caps_to_string(self->dev_caps); GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); @@ -623,6 +763,7 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); if (io_error != G_IO_ERROR_NONE) { GST_ERROR_OBJECT(self, "Error while asking device caps"); + return FALSE; } io_error = gst_a2dp_sender_sink_audioservice_expect(self, @@ -648,6 +789,71 @@ static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) return TRUE; } +static gint gst_a2dp_sender_sink_get_channel_mode(const gchar *mode) +{ + if (strcmp(mode, "stereo") == 0) + return BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(mode, "joint") == 0) + return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (strcmp(mode, "dual") == 0) + return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(mode, "mono") == 0) + return BT_A2DP_CHANNEL_MODE_MONO; + else + return -1; +} + +static void gst_a2dp_sender_sink_tag(const GstTagList *taglist, + const gchar* tag, gpointer user_data) +{ + gboolean crc; + gchar *channel_mode = NULL; + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(user_data); + + if (strcmp(tag, "has-crc") == 0) { + + if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { + GST_WARNING_OBJECT(self, "failed to get crc tag"); + self->mpeg_stream_changed = TRUE; + } + + gst_a2dp_sender_sink_set_crc(self, crc); + + } else if (strcmp(tag, "channel-mode") == 0) { + + if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { + GST_WARNING_OBJECT(self, + "failed to get channel-mode tag"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = gst_a2dp_sender_sink_get_channel_mode( + channel_mode); + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", channel_mode); + g_free(channel_mode); + + } else + GST_DEBUG_OBJECT(self, "received unused tag: %s", tag); +} + +static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink, + GstEvent *event) +{ + GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); + GstTagList *taglist = NULL; + + if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + /* we check the tags, mp3 has tags that are importants and + * are outside caps */ + gst_event_parse_tag(event, &taglist); + gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self); + } + + return TRUE; +} + static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) { GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); @@ -675,9 +881,15 @@ static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) self->stream = NULL; self->stream_caps = NULL; + self->mp3_using_crc = -1; + self->channel_mode = -1; + self->mpeg_stream_changed = FALSE; - if (!gst_a2dp_sender_sink_get_capabilities(self)) + if (!gst_a2dp_sender_sink_get_capabilities(self)) { + GST_ERROR_OBJECT(self, "failed to get capabilities " + "from device"); goto failed; + } return TRUE; @@ -739,6 +951,77 @@ static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self) return TRUE; } +static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf( + GstA2dpSenderSink *self, GstCaps *caps, + mpeg_capabilities_t *pkt) +{ + const GValue *value = NULL; + gint rate, layer; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + /* layer */ + value = gst_structure_get_value(structure, "layer"); + layer = g_value_get_int(value); + if (layer == 1) + pkt->layer = BT_MPEG_LAYER_1; + else if (layer == 2) + pkt->layer = BT_MPEG_LAYER_2; + else if (layer == 3) + pkt->layer = BT_MPEG_LAYER_3; + else { + GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer); + return FALSE; + } + + /* crc */ + if (self->mp3_using_crc != -1) + pkt->crc = self->mp3_using_crc; + else { + GST_ERROR_OBJECT(self, "No info about crc was received, " + " can't proceed"); + return FALSE; + } + + /* channel mode */ + if (self->channel_mode != -1) + pkt->channel_mode = self->channel_mode; + else { + GST_ERROR_OBJECT(self, "No info about channel mode " + "received, can't proceed"); + return FALSE; + } + + /* mpf - we will only use the mandatory one */ + pkt->mpf = 0; + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100; + else if (rate == 48000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000; + else if (rate == 32000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000; + else if (rate == 24000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000; + else if (rate == 22050) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050; + else if (rate == 16000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(self, "Invalid rate while setting caps"); + return FALSE; + } + + /* vbr - we always say its vbr, we don't have how to know it */ + pkt->bitrate = 0x8000; + + /* bitrate - we don't set anything, its vbr */ + /* FIXME - is this right? */ + + return TRUE; +} + static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, GstCaps *caps) { @@ -748,6 +1031,7 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, gboolean ret; GIOError io_error; gchar *temp; + GstStructure *structure; temp = gst_caps_to_string(caps); GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); @@ -757,8 +1041,17 @@ static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, req->h.msg_type = BT_SETCONFIGURATION_REQ; req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; strncpy(req->device, self->device, 18); - ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, - &req->sbc_capabilities); + structure = gst_caps_get_structure(caps, 0); + + if (gst_structure_has_name(structure, "audio/x-sbc")) + ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, + &req->sbc_capabilities); + else if (gst_structure_has_name(structure, "audio/mpeg")) + ret = gst_a2dp_sender_sink_init_mp3_pkt_conf(self, caps, + &req->mpeg_capabilities); + else + ret = FALSE; + if (!ret) { GST_ERROR_OBJECT(self, "Couldn't parse caps " "to packet configuration"); @@ -884,6 +1177,8 @@ static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass) gst_a2dp_sender_sink_preroll); basesink_class->unlock = GST_DEBUG_FUNCPTR( gst_a2dp_sender_sink_unlock); + basesink_class->event = GST_DEBUG_FUNCPTR( + gst_a2dp_sender_sink_event); basesink_class->buffer_alloc = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc); @@ -1022,3 +1317,39 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self) return g_strdup(self->device); } +void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc) +{ + gint new_crc; + + new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED; + + /* test if we already received a different crc */ + if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { + GST_ERROR_OBJECT(self, "crc changed during stream"); + /* FIXME test this, its not being used anywhere */ + self->mpeg_stream_changed = TRUE; + return; + } + self->mp3_using_crc = new_crc; + +} + +void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, + const gchar *mode) +{ + gint new_mode; + + new_mode = gst_a2dp_sender_sink_get_channel_mode(mode); + + if (self->channel_mode != -1 && new_mode != self->channel_mode) { + GST_ERROR_OBJECT(self, "channel mode changed during stream"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = new_mode; + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", mode); +} + + diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h index 863ef6cb..f23c86b2 100644 --- a/audio/gsta2dpsendersink.h +++ b/audio/gsta2dpsendersink.h @@ -56,6 +56,11 @@ struct _GstA2dpSenderSink { struct bluetooth_data *data; GIOChannel *server; + /* mp3 stream data (outside caps data)*/ + gboolean mpeg_stream_changed; + gint mp3_using_crc; + gint channel_mode; + /* stream connection data */ GstCaps *stream_caps; @@ -85,6 +90,12 @@ gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink); gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin); +void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc); + +void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, + const gchar *mode); + + G_END_DECLS #endif /* __GST_A2DP_SENDER_SINK_H */ diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index d90909c7..091d5472 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -61,8 +61,14 @@ static GstStaticPadTemplate gst_a2dp_sink_factory = "allocation = (string) { snr, loudness }, " "bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; " + "audio/mpeg;" )); +static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event); +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); +static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); +static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -116,30 +122,100 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, } } +static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self) +{ + GstPad *capsfilter_pad; + + /* we search for the capsfilter sinkpad */ + capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); + + /* now we add a ghostpad */ + self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", + capsfilter_pad)); + g_object_unref(capsfilter_pad); + + /* the getcaps of our ghostpad must reflect the device caps */ + gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_get_caps); + self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); + gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), + GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); + + /* we need to handle events on our own and we also need the eventfunc + * of the ghostpad for forwarding calls */ + self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); + gst_pad_set_event_function(GST_PAD(self->ghostpad), + gst_a2dp_sink_handle_event); + + if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) + GST_ERROR_OBJECT(self, "failed to add ghostpad"); + + return TRUE; +} + static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, GstStateChange transition) { + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; GstA2dpSink *self = GST_A2DP_SINK(element); switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->taglist = gst_tag_list_new(); + break; + case GST_STATE_CHANGE_NULL_TO_READY: - gst_element_set_state(GST_ELEMENT(self->sink), + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( + "a2dpsendersink", "sendersink")); + if (self->sink == NULL) { + GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); + return GST_STATE_CHANGE_FAILURE; + } + + if (self->device != NULL) + gst_a2dp_sender_sink_set_device(self->sink, + self->device); + + ret = gst_element_set_state(GST_ELEMENT(self->sink), GST_STATE_READY); break; + default: + break; + } - case GST_STATE_CHANGE_READY_TO_NULL: + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, + transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (self->taglist) { + gst_tag_list_free(self->taglist); + self->taglist = NULL; + } if (self->newseg_event != NULL) { gst_event_unref(self->newseg_event); self->newseg_event = NULL; } + if (self->taglist) { + gst_tag_list_free(self->taglist); + self->taglist = NULL; + } + break; + + case GST_STATE_CHANGE_READY_TO_NULL: + if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) + GST_WARNING_OBJECT(self, "Failed to remove " + "a2dpsendersink from bin"); break; default: break; } - return GST_ELEMENT_CLASS(parent_class)->change_state(element, - transition); + return ret; } static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) @@ -280,6 +356,54 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) return TRUE; +remove_element_and_fail: + gst_element_set_state(rtppay, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), rtppay); + return FALSE; + +cleanup_and_fail: + if (rtppay != NULL) + g_object_unref(G_OBJECT(rtppay)); + + return FALSE; +} + +static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) +{ + GstElement *rtppay; + + /* FIXME we will need a internal mpegparse for identifying + * stream stuff */ + + rtppay = gst_element_factory_make("rtpmpapay", "rtp"); + if (rtppay == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay"); + return FALSE; + } + + if (!gst_bin_add(GST_BIN(self), rtppay)) { + GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin"); + goto cleanup_and_fail; + } + + if (gst_element_set_state(rtppay, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready"); + goto remove_element_and_fail; + } + + if (!gst_element_link(self->capsfilter, rtppay)) { + GST_ERROR_OBJECT(self, "couldn't link capsfilter " + "to rtpmpapay"); + goto remove_element_and_fail; + } + + self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); + + gst_element_set_state(rtppay, GST_STATE_PAUSED); + + return TRUE; + remove_element_and_fail: gst_element_set_state (rtppay, GST_STATE_NULL); gst_bin_remove(GST_BIN(self), rtppay); @@ -296,6 +420,10 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) { GstA2dpSink *self; GstStructure *structure; + GstEvent *event; + GstPad *capsfilterpad; + gboolean crc; + gchar *mode = NULL; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); GST_INFO_OBJECT(self, "setting caps"); @@ -303,9 +431,13 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) structure = gst_caps_get_structure(caps, 0); /* first, we need to create our rtp payloader */ - if (gst_structure_has_name(structure, "audio/x-sbc")) - gst_a2dp_sink_init_rtp_sbc_element(self); - else { + if (gst_structure_has_name(structure, "audio/x-sbc")) { + if (!gst_a2dp_sink_init_rtp_sbc_element(self)) + return FALSE; + } else if (gst_structure_has_name(structure, "audio/mpeg")) { + if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) + return FALSE; + } else { GST_ERROR_OBJECT(self, "Unexpected media type"); return FALSE; } @@ -313,6 +445,27 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) if (!gst_a2dp_sink_init_sender_sink(self)) return FALSE; + /* check if we should push the taglist FIXME should we push this? + * we can send the tags directly if needed */ + if (self->taglist != NULL && + gst_structure_has_name(structure, "audio/mpeg")) { + + event = gst_event_new_tag(self->taglist); + + /* send directly the crc */ + if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc)) + gst_a2dp_sender_sink_set_crc(self->sink, crc); + + if (gst_tag_list_get_string(self->taglist, "channel-mode", + &mode)) + gst_a2dp_sender_sink_set_channel_mode(self->sink, mode); + + capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); + gst_pad_send_event(capsfilterpad, event); + self->taglist = NULL; + g_free(mode); + } + if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) return FALSE; @@ -332,6 +485,7 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) { GstA2dpSink *self; + GstTagList *taglist = NULL; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); @@ -341,6 +495,17 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) if (self->newseg_event != NULL) gst_event_unref(self->newseg_event); self->newseg_event = gst_event_ref(event); + } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && + gst_element_get_parent(GST_ELEMENT(self->sink)) != + GST_OBJECT_CAST(self)) { + if (self->taglist == NULL) { + gst_event_parse_tag(event, &self->taglist); + } else { + gst_event_parse_tag(event, &taglist); + gst_tag_list_insert(self->taglist, taglist, + GST_TAG_MERGE_REPLACE); + } + /* FIXME handle tag events */ } return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); @@ -368,13 +533,14 @@ failed: static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { - GstPad *capsfilter_pad; - self->sink = NULL; self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; + self->mpegparse = NULL; self->newseg_event = NULL; + self->taglist = NULL; + self->ghostpad = NULL; /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); @@ -382,34 +548,8 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), NULL); - /* we search for the capsfilter sinkpad */ - capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); - - /* now we add a ghostpad */ - self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", - capsfilter_pad)); - g_object_unref(capsfilter_pad); + gst_a2dp_sink_init_ghost_pad(self); - /* the getcaps of our ghostpad must reflect the device caps */ - gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_get_caps); - self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); - gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), - GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); - - /* we need to handle events on our own and we also need the eventfunc - * of the ghostpad for forwarding calls */ - self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); - gst_pad_set_event_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_handle_event); - - if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) - GST_ERROR_OBJECT(self, "failed to add ghostpad"); - - self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( - "a2dpsendersink", "sendersink")); - if (self->sink == NULL) - GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); } gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin) diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 4bf9d603..f74185ef 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -47,6 +47,7 @@ struct _GstA2dpSink { GstBaseRTPPayload *rtp; GstA2dpSenderSink *sink; GstElement *capsfilter; + GstElement *mpegparse; gchar *device; @@ -55,6 +56,9 @@ struct _GstA2dpSink { GstPadEventFunction ghostpad_eventfunc; GstEvent *newseg_event; + /* Store the tags received before the a2dpsender sink is created + * when it is created we forward this to it */ + GstTagList *taglist; }; struct _GstA2dpSinkClass { diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 764bc899..eaab23d2 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -25,6 +25,11 @@ #include #endif +#include + +#include "gstsbcutil.h" +#include + #include "gstsbcenc.h" #include "gstsbcdec.h" #include "gstsbcparse.h" @@ -38,12 +43,26 @@ static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); static void sbc_typefind(GstTypeFind *tf, gpointer ignore) { - guint8 *data = gst_type_find_peek(tf, 0, 1); + GstCaps *caps; + guint8 *aux; + sbc_t sbc; + guint8 *data = gst_type_find_peek(tf, 0, 32); + + if (sbc_init(&sbc, 0) < 0) + return; if (data == NULL || *data != 0x9c) /* SBC syncword */ return; - gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, SBC_CAPS); + aux = g_new(guint8, 32); + memcpy(aux, data, 32); + sbc_parse(&sbc, aux, 32); + g_free(aux); + caps = gst_sbc_parse_caps_from_sbc(&sbc); + sbc_finish(&sbc); + + gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps); + gst_caps_unref(caps); } static gchar *sbc_exts[] = { "sbc", NULL }; diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index d791a8d6..04b7217a 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -89,12 +89,22 @@ const gchar *gst_sbc_get_allocation_from_list(const GValue *value) /* * Selects one mode from the ones on the list - * TODO - use a better aproach */ -const gchar *gst_sbc_get_mode_from_list(const GValue *value) +const gchar *gst_sbc_get_mode_from_list(const GValue *list) { - guint size = gst_value_list_get_size(value); - return g_value_get_string(gst_value_list_get_value(value, size-1)); + int i; + const GValue *value; + const gchar *aux; + + guint size = gst_value_list_get_size(list); + for (i = 0; i < size; i++) { + value = gst_value_list_get_value(list, i); + aux = g_value_get_string(value); + if (strcmp("stereo", aux) == 0) { + return "stereo"; + } + } + return g_value_get_string(gst_value_list_get_value(list, size-1)); } gint gst_sbc_get_allocation_mode_int(const gchar *allocation) @@ -157,6 +167,61 @@ const gchar *gst_sbc_get_allocation_string(int alloc) } } +/* channel mode */ +#define SBC_CM_MONO 0x00 +#define SBC_CM_DUAL_CHANNEL 0x01 +#define SBC_CM_STEREO 0x02 +#define SBC_CM_JOINT_STEREO 0x03 + +/* allocation mode */ +#define SBC_AM_LOUDNESS 0x00 +#define SBC_AM_SNR 0x01 + +const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint) +{ + if (channels == 2 && joint == 1) + return "joint"; + else if (channels == 2 && joint == 0) + return "stereo"; + else + return NULL; +} + +const gchar *gst_sbc_get_allocation_string_from_sbc_t(int alloc) +{ + switch (alloc) { + case SBC_AM_LOUDNESS: + return "loudness"; + case SBC_AM_SNR: + return "snr"; + default: + return NULL; + } +} + +GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc) +{ + GstCaps *caps; + const gchar *mode_str; + const gchar *allocation_str; + + mode_str = gst_sbc_get_mode_string_from_sbc_t(sbc->channels, + sbc->joint); + allocation_str = gst_sbc_get_allocation_string_from_sbc_t( + sbc->allocation); + caps = gst_caps_new_simple("audio/x-sbc", + "rate", G_TYPE_INT, sbc->rate, + "channels", G_TYPE_INT, sbc->channels, + "mode", G_TYPE_STRING, mode_str, + "subbands", G_TYPE_INT, sbc->subbands, + "blocks", G_TYPE_INT, sbc->blocks, + "allocation", G_TYPE_STRING, allocation_str, + "bitpool", G_TYPE_INT, sbc->bitpool, + NULL); + + return caps; +} + GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels) { GstCaps *caps; diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index a67bf1be..4bdb5ac5 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -47,6 +47,7 @@ gint gst_sbc_get_mode_int(const gchar *mode); const gchar *gst_sbc_get_mode_string(int joint); GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); +GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc); GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); -- cgit From ad834a766f5fa52cb08280fe9a297a6f46359a70 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:21:57 +0000 Subject: Make debug logs per sep. --- audio/a2dp.c | 85 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 42 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index b1409f5c..01738118 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -294,9 +294,9 @@ static gboolean sbc_setconf_ind(struct avdtp *session, struct sbc_codec_cap *sbc_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Ind"); + debug("Sink %p: Set_Configuration_Ind", sep); else - debug("SBC Source: Set_Configuration_Ind"); + debug("Source %p: Set_Configuration_Ind", sep); dev = a2dp_get_dev(session); if (!dev) { @@ -340,9 +340,9 @@ static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *se struct sbc_codec_cap sbc_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Capability_Ind"); + debug("Sink %p: Get_Capability_Ind", sep); else - debug("SBC Source: Get_Capability_Ind"); + debug("Source %p: Get_Capability_Ind", sep); *caps = NULL; @@ -397,9 +397,9 @@ static gboolean mpeg_setconf_ind(struct avdtp *session, struct device *dev; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Ind"); + debug("Sink %p: Set_Configuration_Ind", sep); else - debug("SBC Source: Set_Configuration_Ind"); + debug("Source %p: Set_Configuration_Ind", sep); dev = a2dp_get_dev(session); if (!dev) { @@ -417,7 +417,8 @@ static gboolean mpeg_setconf_ind(struct avdtp *session, return TRUE; } -static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, +static gboolean mpeg_getcap_ind(struct avdtp *session, + struct avdtp_local_sep *sep, GSList **caps, uint8_t *err, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; @@ -425,9 +426,9 @@ static gboolean mpeg_getcap_ind(struct avdtp *session, struct avdtp_local_sep *s struct mpeg_codec_cap mpeg_cap; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Capability_Ind"); + debug("Sink %p: Get_Capability_Ind", sep); else - debug("SBC Source: Get_Capability_Ind"); + debug("Source %p: Get_Capability_Ind", sep); *caps = NULL; @@ -475,9 +476,9 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Cfm"); + debug("Sink %p: Set_Configuration_Cfm", sep); else - debug("SBC Source: Set_Configuration_Cfm"); + debug("Source %p: Set_Configuration_Cfm", sep); setup = find_setup_by_session(session); @@ -515,9 +516,9 @@ static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Get_Configuration_Ind"); + debug("Sink %p: Get_Configuration_Ind"); else - debug("SBC Source: Get_Configuration_Ind"); + debug("Source %p: Get_Configuration_Ind"); return TRUE; } @@ -528,9 +529,9 @@ static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Set_Configuration_Cfm"); + debug("Sink %p: Set_Configuration_Cfm", sep); else - debug("SBC Source: Set_Configuration_Cfm"); + debug("Source %p: Set_Configuration_Cfm", sep); } static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -540,9 +541,9 @@ static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Open_Ind"); + debug("Sink %p: Open_Ind", sep); else - debug("SBC Source: Open_Ind"); + debug("Source %p: Open_Ind", sep); return TRUE; } @@ -554,9 +555,9 @@ static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Open_Cfm"); + debug("Sink %p: Open_Cfm", sep); else - debug("SBC Source: Open_Cfm"); + debug("Source %p: Open_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -601,9 +602,9 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Start_Ind"); + debug("Sink %p: Start_Ind", sep); else - debug("SBC Source: Start_Ind"); + debug("Source %p: Start_Ind", sep); if (!a2dp_sep->locked) { a2dp_sep->session = avdtp_ref(session); @@ -623,9 +624,9 @@ static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Start_Cfm"); + debug("Sink %p: Start_Cfm", sep); else - debug("SBC Source: Start_Cfm"); + debug("Source %p: Start_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -654,9 +655,9 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Suspend_Ind"); + debug("Sink %p: Suspend_Ind", sep); else - debug("SBC Source: Suspend_Ind"); + debug("Source %p: Suspend_Ind", sep); if (a2dp_sep->suspend_timer) { g_source_remove(a2dp_sep->suspend_timer); @@ -677,9 +678,9 @@ static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, gboolean start; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Suspend_Cfm"); + debug("Sink %p: Suspend_Cfm", sep); else - debug("SBC Source: Suspend_Cfm"); + debug("Source %p: Suspend_Cfm", sep); a2dp_sep->suspending = FALSE; @@ -720,9 +721,9 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Close_Ind"); + debug("Sink %p: Close_Ind", sep); else - debug("SBC Source: Close_Ind"); + debug("Source %p: Close_Ind", sep); return TRUE; } @@ -762,9 +763,9 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Close_Cfm"); + debug("Sink %p: Close_Cfm", sep); else - debug("SBC Source: Close_Cfm"); + debug("Source %p: Close_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -793,9 +794,9 @@ static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Abort_Ind"); + debug("Sink %p: Abort_Ind", sep); else - debug("SBC Source: Abort_Ind"); + debug("Source %p: Abort_Ind", sep); a2dp_sep->stream = NULL; @@ -810,9 +811,9 @@ static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: Abort_Cfm"); + debug("Sink %p: Abort_Cfm", sep); else - debug("SBC Source: Abort_Cfm"); + debug("Source %p: Abort_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -827,9 +828,9 @@ static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: ReConfigure_Ind"); + debug("Sink %p: ReConfigure_Ind", sep); else - debug("SBC Source: ReConfigure_Ind"); + debug("Source %p: ReConfigure_Ind", sep); return TRUE; } @@ -841,9 +842,9 @@ static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - debug("SBC Sink: ReConfigure_Cfm"); + debug("Sink %p: ReConfigure_Cfm", sep); else - debug("SBC Source: ReConfigure_Cfm"); + debug("Source %p: ReConfigure_Cfm", sep); setup = find_setup_by_session(session); if (!setup) @@ -1161,7 +1162,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, return 0; } - debug("a2dp_source_config: selected SEP %p", sep); + debug("a2dp_source_config: selected SEP %p", sep->sep); cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->config_cb = cb; @@ -1337,7 +1338,7 @@ gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session) if (sep->locked) return FALSE; - debug("SBC Source SEP %p locked", sep); + debug("SEP %p locked", sep->sep); sep->locked = TRUE; return TRUE; @@ -1351,7 +1352,7 @@ gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) sep->locked = FALSE; - debug("SBC Source SEP %p unlocked", sep); + debug("SEP %p unlocked", sep->sep); if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; -- cgit From f978653b315bc6d9d029dae1206085ba579fced4 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:23:01 +0000 Subject: Bug fixes for gstreamer plugin. --- audio/gsta2dpsink.c | 194 ++++++++++++++++++++++++++++++---------------------- audio/gsta2dpsink.h | 2 +- 2 files changed, 114 insertions(+), 82 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 091d5472..7c68d17f 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -69,6 +69,56 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); +/* + * Helper function to create elements, add to the bin and link it + * to another element. + */ +static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, + const gchar* elementname, const gchar* name, + GstElement *link_to) +{ + GstElement *element; + + GST_LOG_OBJECT(self, "Initializing %s", elementname); + + element = gst_element_factory_make(elementname, name); + if (element == NULL) { + GST_ERROR_OBJECT(self, "Couldn't create %s", elementname); + return NULL; + } + + if (!gst_bin_add(GST_BIN(self), element)) { + GST_ERROR_OBJECT(self, "failed to add %s to the bin", + elementname); + goto cleanup_and_fail; + } + + if (gst_element_set_state(element, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE) { + GST_ERROR_OBJECT(self, "%s failed to go to ready", + elementname); + goto remove_element_and_fail; + } + + if (!gst_element_link(link_to, element)) { + GST_ERROR_OBJECT(self, "couldn't link %s", elementname); + goto remove_element_and_fail; + } + + return element; + +remove_element_and_fail: + gst_element_set_state(element, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), element); + return NULL; + +cleanup_and_fail: + if (element != NULL) + g_object_unref(G_OBJECT(element)); + + return NULL; +} + static void gst_a2dp_sink_base_init(gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); @@ -153,6 +203,18 @@ static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self) return TRUE; } +static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self) +{ + if (self->rtp) { + GST_LOG_OBJECT(self, "removing rtp element from the bin"); + if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp))) + GST_WARNING_OBJECT(self, "failed to remove rtp " + "element from bin"); + else + self->rtp = NULL; + } +} + static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, GstStateChange transition) { @@ -165,6 +227,8 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, break; case GST_STATE_CHANGE_NULL_TO_READY: + self->sink_is_in_bin = FALSE; + self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( "a2dpsendersink", "sendersink")); if (self->sink == NULL) { @@ -199,16 +263,24 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_event_unref(self->newseg_event); self->newseg_event = NULL; } - if (self->taglist) { - gst_tag_list_free(self->taglist); - self->taglist = NULL; - } break; case GST_STATE_CHANGE_READY_TO_NULL: - if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) - GST_WARNING_OBJECT(self, "Failed to remove " - "a2dpsendersink from bin"); + if (self->sink_is_in_bin) { + if (!gst_bin_remove(GST_BIN(self), + GST_ELEMENT(self->sink))) + GST_WARNING_OBJECT(self, "Failed to remove " + "a2dpsendersink from bin"); + } else if (self->sink != NULL) { + gst_element_set_state(GST_ELEMENT(self->sink), + GST_STATE_NULL); + g_object_unref(G_OBJECT(self->sink)); + } + + self->sink = NULL; + + gst_a2dp_sink_remove_dynamic_elements(self); + break; default: @@ -304,6 +376,7 @@ static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) } self->sink = GST_A2DP_SENDER_SINK(sink); + self->sink_is_in_bin = TRUE; g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); gst_element_set_state(sink, GST_STATE_PAUSED); @@ -326,28 +399,10 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) { GstElement *rtppay; - rtppay = gst_element_factory_make("rtpsbcpay", "rtp"); - if (rtppay == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create rtpsbcpay"); + rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp", + self->capsfilter); + if (rtppay == NULL) return FALSE; - } - - if (!gst_bin_add(GST_BIN(self), rtppay)) { - GST_ERROR_OBJECT(self, "failed to add rtp sbc pay to the bin"); - goto cleanup_and_fail; - } - - if (gst_element_set_state(rtppay, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "rtpsbcpay failed to go to ready"); - goto remove_element_and_fail; - } - - if (!gst_element_link(self->capsfilter, rtppay)) { - GST_ERROR_OBJECT(self, "couldn't link capsfilter " - "to rtpsbcpay"); - goto remove_element_and_fail; - } self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); @@ -355,86 +410,48 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; - -remove_element_and_fail: - gst_element_set_state(rtppay, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), rtppay); - return FALSE; - -cleanup_and_fail: - if (rtppay != NULL) - g_object_unref(G_OBJECT(rtppay)); - - return FALSE; } static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) { GstElement *rtppay; - /* FIXME we will need a internal mpegparse for identifying - * stream stuff */ + GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); - rtppay = gst_element_factory_make("rtpmpapay", "rtp"); - if (rtppay == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create rtpmpapay"); + /* if capsfilter is not created then we can't have our rtp element */ + if (self->capsfilter == NULL) return FALSE; - } - if (!gst_bin_add(GST_BIN(self), rtppay)) { - GST_ERROR_OBJECT(self, "failed to add rtpmpapay to the bin"); - goto cleanup_and_fail; - } - - if (gst_element_set_state(rtppay, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "rtpmpapay failed to go to ready"); - goto remove_element_and_fail; - } - - if (!gst_element_link(self->capsfilter, rtppay)) { - GST_ERROR_OBJECT(self, "couldn't link capsfilter " - "to rtpmpapay"); - goto remove_element_and_fail; - } + rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", + self->capsfilter); + if (rtppay == NULL) + return FALSE; self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE; - -remove_element_and_fail: - gst_element_set_state (rtppay, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), rtppay); - return FALSE; - -cleanup_and_fail: - if (rtppay != NULL) - g_object_unref(G_OBJECT(rtppay)); - - return FALSE; } -static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) +static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, + GstCaps *caps) { - GstA2dpSink *self; GstStructure *structure; GstEvent *event; GstPad *capsfilterpad; gboolean crc; gchar *mode = NULL; - self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - GST_INFO_OBJECT(self, "setting caps"); - structure = gst_caps_get_structure(caps, 0); /* first, we need to create our rtp payloader */ if (gst_structure_has_name(structure, "audio/x-sbc")) { + GST_LOG_OBJECT(self, "sbc media received"); if (!gst_a2dp_sink_init_rtp_sbc_element(self)) return FALSE; } else if (gst_structure_has_name(structure, "audio/mpeg")) { + GST_LOG_OBJECT(self, "mp3 media received"); if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) return FALSE; } else { @@ -473,9 +490,24 @@ static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL); /* we forward our new segment here if we have one */ - gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), - self->newseg_event); - self->newseg_event = NULL; + if (self->newseg_event) { + gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), + self->newseg_event); + self->newseg_event = NULL; + } + + return TRUE; +} + +static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) +{ + GstA2dpSink *self; + + self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + GST_INFO_OBJECT(self, "setting caps"); + + /* now we know the caps */ + gst_a2dp_sink_init_dynamic_elements(self, caps); return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps); } @@ -537,10 +569,10 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; - self->mpegparse = NULL; self->newseg_event = NULL; self->taglist = NULL; self->ghostpad = NULL; + self->sink_is_in_bin = FALSE; /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index f74185ef..368f837a 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -47,9 +47,9 @@ struct _GstA2dpSink { GstBaseRTPPayload *rtp; GstA2dpSenderSink *sink; GstElement *capsfilter; - GstElement *mpegparse; gchar *device; + gboolean sink_is_in_bin; GstGhostPad *ghostpad; GstPadSetCapsFunction ghostpad_setcapsfunc; -- cgit From 5b601136563565049e0dc726b67860bc636e03bd Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 13:27:30 +0000 Subject: Add config options for each source. --- audio/a2dp.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++------- audio/a2dp.h | 2 +- audio/audio.conf | 3 +- audio/avdtp.c | 2 ++ audio/manager.c | 28 +++-------------- 5 files changed, 90 insertions(+), 36 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 01738118..fa5ff2bf 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -969,7 +969,7 @@ static int a2dp_source_record(sdp_buf_t *buf) static int a2dp_sink_record(sdp_buf_t *buf) { - return 0; + return -1; } static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, @@ -1008,6 +1008,7 @@ static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, if (*record_id != 0) goto add; + memset(&buf, 0, sizeof(buf)); if (create_record(&buf) < 0) { error("Unable to allocate new service record"); avdtp_unregister_sep(sep->sep); @@ -1030,25 +1031,95 @@ add: return sep; } -int a2dp_init(DBusConnection *conn, int sources, int sinks) +int a2dp_init(DBusConnection *conn, GKeyFile *config) { + int sbc_srcs = 1, sbc_sinks = 0; + int mpeg12_srcs = 0, mpeg12_sinks = 0; + gboolean src = TRUE, sink = TRUE; + char *str; + GError *err = NULL; int i; - if (!sources && !sinks) - return 0; + if (!config) + goto proceed; + + str = g_key_file_get_string(config, "General", "Disable", &err); + + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + if (strstr(str, "Sink")) + sink = FALSE; + if (strstr(str, "Source")) + src = FALSE; + g_free(str); + } + str = g_key_file_get_string(config, "A2DP", "SBCSources", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + sbc_srcs = atoi(str); + g_free(str); + } + + str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + mpeg12_srcs = atoi(str); + g_free(str); + } + + str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + sbc_sinks = atoi(str); + g_free(str); + } + + str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else { + mpeg12_sinks = atoi(str); + g_free(str); + } + +proceed: connection = dbus_connection_ref(conn); avdtp_init(); - for (i = 0; i < sources; i++) { - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC); - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_MPEG12); + if (src) { + for (i = 0; i < sbc_srcs; i++) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, + A2DP_CODEC_SBC); + + for (i = 0; i < mpeg12_srcs; i++) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, + A2DP_CODEC_MPEG12); } - for (i = 0; i < sinks; i++) { - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_SBC); - a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_MPEG12); + if (sink) { + for (i = 0; i < sbc_sinks; i++) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, + A2DP_CODEC_SBC); + + for (i = 0; i < mpeg12_sinks; i++) + a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, + A2DP_CODEC_MPEG12); } return 0; diff --git a/audio/a2dp.h b/audio/a2dp.h index 84301bad..a83b523a 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -129,7 +129,7 @@ typedef void (*a2dp_stream_cb_t) (struct avdtp *session, struct avdtp_error *err, void *user_data); -int a2dp_init(DBusConnection *conn, int sources, int sinks); +int a2dp_init(DBusConnection *conn, GKeyFile *config); void a2dp_exit(void); unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, diff --git a/audio/audio.conf b/audio/audio.conf index ae9742b8..0d733105 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -34,4 +34,5 @@ ExtendedErrorResultCodes=false # Just an example of potential config options for the other interfaces #[A2DP] -#SourceCount=2 +#SBCSources=1 +#MPEG12Sources=0 diff --git a/audio/avdtp.c b/audio/avdtp.c index 97868a07..55addb0b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2717,6 +2717,8 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, sep->cfm = cfm; sep->user_data = user_data; + debug("SEP %p registered: type:%d codec:%d seid:%d", sep, + sep->info.type, sep->codec, sep->info.seid); local_seps = g_slist_append(local_seps, sep); return sep; diff --git a/audio/manager.c b/audio/manager.c index 931af069..1c8970d1 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1634,7 +1634,6 @@ static void server_exit(void) int audio_init(DBusConnection *conn, GKeyFile *config) { - int sinks, sources; char *str; GError *err = NULL; @@ -1693,29 +1692,10 @@ int audio_init(DBusConnection *conn, GKeyFile *config) goto failed; } - if (enabled.sink) { - if (config) { - str = g_key_file_get_string(config, "A2DP", - "SourceCount", &err); - if (err) { - debug("audio.conf: %s", err->message); - g_error_free(err); - err = NULL; - } else { - sources = atoi(str); - g_free(str); - } - } - } else - sources = 0; - - if (enabled.source) - sinks = 1; - else - sinks = 0; - - if (a2dp_init(conn, sources, sinks) < 0) - goto failed; + if (enabled.source || enabled.sink) { + if (a2dp_init(conn, config) < 0) + goto failed; + } if (enabled.control && avrcp_init(conn) < 0) goto failed; -- cgit From e8e68d294cd662970de6082473e30ecd6ee8dd61 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 15:18:15 +0000 Subject: Rename a2dpsendersink to avdtpsink. --- audio/Makefile.am | 2 +- audio/gsta2dpsendersink.c | 1355 --------------------------------------------- audio/gsta2dpsendersink.h | 101 ---- audio/gsta2dpsink.c | 36 +- audio/gsta2dpsink.h | 4 +- audio/gstavdtpsink.c | 1355 +++++++++++++++++++++++++++++++++++++++++++++ audio/gstavdtpsink.h | 101 ++++ audio/gstbluetooth.c | 4 +- 8 files changed, 1479 insertions(+), 1479 deletions(-) delete mode 100644 audio/gsta2dpsendersink.c delete mode 100644 audio/gsta2dpsendersink.h create mode 100644 audio/gstavdtpsink.c create mode 100644 audio/gstavdtpsink.h (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 5d838c85..e36c2c2d 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -43,7 +43,7 @@ libgstbluetooth_la_SOURCES = gstbluetooth.c \ gstsbcenc.h gstsbcenc.c \ gstsbcdec.h gstsbcdec.c \ gstsbcparse.h gstsbcparse.c \ - gsta2dpsendersink.h gsta2dpsendersink.c \ + gstavdtpsink.h gstavdtpsink.c \ gsta2dpsink.h gsta2dpsink.c \ gstsbcutil.h gstsbcutil.c \ gstrtpsbcpay.h gstrtpsbcpay.c \ diff --git a/audio/gsta2dpsendersink.c b/audio/gsta2dpsendersink.c deleted file mode 100644 index 0256bb93..00000000 --- a/audio/gsta2dpsendersink.c +++ /dev/null @@ -1,1355 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2007 Marcel Holtmann - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; 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 "ipc.h" -#include "rtp.h" -#include "gstsbcutil.h" - -#include "gsta2dpsendersink.h" - -GST_DEBUG_CATEGORY_STATIC(a2dp_sender_sink_debug); -#define GST_CAT_DEFAULT a2dp_sender_sink_debug - -#define BUFFER_SIZE 2048 -#define TEMPLATE_MAX_BITPOOL 64 -#define CRC_PROTECTED 1 -#define CRC_UNPROTECTED 0 - -#define GST_A2DP_SENDER_SINK_MUTEX_LOCK(s) G_STMT_START { \ - g_mutex_lock (s->sink_lock); \ - } G_STMT_END - -#define GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ - g_mutex_unlock (s->sink_lock); \ - } G_STMT_END - - -struct bluetooth_data { - struct bt_getcapabilities_rsp caps; /* Bluetooth device caps */ - guint link_mtu; - - gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ -}; - -#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) -#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) - -enum { - PROP_0, - PROP_DEVICE, -}; - -GST_BOILERPLATE(GstA2dpSenderSink, gst_a2dp_sender_sink, GstBaseSink, - GST_TYPE_BASE_SINK); - -static const GstElementDetails a2dp_sender_sink_details = - GST_ELEMENT_DETAILS("Bluetooth A2DP sink", - "Sink/Audio", - "Plays audio to an A2DP device", - "Marcel Holtmann "); - -static GstStaticPadTemplate a2dp_sender_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("application/x-rtp, " - "media = (string) \"audio\", " - "encoding-name = (string) \"SBC\";" - "application/x-rtp, " - "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_MPA_STRING ", " - "clock-rate = (int) 90000; " - "application/x-rtp, " - "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) 90000, " - "encoding-name = (string) \"MPA\"" - )); - -static GIOError gst_a2dp_sender_sink_audioservice_send(GstA2dpSenderSink *self, - const bt_audio_msg_header_t *msg); -static GIOError gst_a2dp_sender_sink_audioservice_expect( - GstA2dpSenderSink *self, - bt_audio_msg_header_t *outmsg, - int expected_type); - - -static void gst_a2dp_sender_sink_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&a2dp_sender_sink_factory)); - - gst_element_class_set_details(element_class, &a2dp_sender_sink_details); -} - -static gboolean gst_a2dp_sender_sink_stop(GstBaseSink *basesink) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - - GST_INFO_OBJECT(self, "stop"); - - if (self->watch_id != 0) { - g_source_remove(self->watch_id); - self->watch_id = 0; - } - - if (self->server) { - bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); - g_io_channel_unref(self->server); - self->server = NULL; - } - - if (self->stream) { - g_io_channel_flush(self->stream, NULL); - g_io_channel_close(self->stream); - g_io_channel_unref(self->stream); - self->stream = NULL; - } - - if (self->data) { - g_free(self->data); - self->data = NULL; - } - - if (self->stream_caps) { - gst_caps_unref(self->stream_caps); - self->stream_caps = NULL; - } - - if (self->dev_caps) { - gst_caps_unref(self->dev_caps); - self->dev_caps = NULL; - } - - return TRUE; -} - -static void gst_a2dp_sender_sink_finalize(GObject *object) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(object); - - if (self->data) - gst_a2dp_sender_sink_stop(GST_BASE_SINK(self)); - - if (self->device) - g_free(self->device); - - g_mutex_free(self->sink_lock); - - G_OBJECT_CLASS(parent_class)->finalize(object); -} - -static void gst_a2dp_sender_sink_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); - - switch (prop_id) { - case PROP_DEVICE: - if (sink->device) - g_free(sink->device); - sink->device = g_value_dup_string(value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_a2dp_sender_sink_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string(value, sink->device); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static gint gst_a2dp_sender_sink_bluetooth_recvmsg_fd(GstA2dpSenderSink *sink) -{ - int err, ret; - - ret = bt_audio_service_get_data_fd( - g_io_channel_unix_get_fd(sink->server)); - - if (ret < 0) { - err = errno; - GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", - strerror(err), err); - return -err; - } - - sink->stream = g_io_channel_unix_new(ret); - GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); - - return 0; -} - -static gboolean gst_a2dp_sender_sink_init_pkt_conf(GstA2dpSenderSink *sink, - GstCaps *caps, - sbc_capabilities_t *pkt) -{ - sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; - const GValue *value = NULL; - const char *pref, *name; - gint rate, subbands, blocks; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - name = gst_structure_get_name(structure); - /* FIXME only sbc supported here, should suport mp3 */ - if (!(IS_SBC(name))) { - GST_ERROR_OBJECT(sink, "Unsupported format %s", name); - return FALSE; - } - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - cfg->frequency = BT_SBC_SAMPLING_FREQ_44100; - else if (rate == 48000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_48000; - else if (rate == 32000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_32000; - else if (rate == 16000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); - return FALSE; - } - - value = gst_structure_get_value(structure, "mode"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else { - GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); - return FALSE; - } - - value = gst_structure_get_value(structure, "allocation"); - pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; - else { - GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); - return FALSE; - } - - value = gst_structure_get_value(structure, "subbands"); - subbands = g_value_get_int(value); - if (subbands == 8) - cfg->subbands = BT_A2DP_SUBBANDS_8; - else if (subbands == 4) - cfg->subbands = BT_A2DP_SUBBANDS_4; - else { - GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); - return FALSE; - } - - value = gst_structure_get_value(structure, "blocks"); - blocks = g_value_get_int(value); - if (blocks == 16) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (blocks == 12) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (blocks == 8) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (blocks == 4) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); - return FALSE; - } - - value = gst_structure_get_value(structure, "bitpool"); - cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - - memcpy(pkt, cfg, sizeof(*pkt)); - - return TRUE; -} - -static gboolean gst_a2dp_sender_sink_conf_recv_stream_fd( - GstA2dpSenderSink *self) -{ - struct bluetooth_data *data = self->data; - gint ret; - GIOError err; - GError *gerr = NULL; - GIOStatus status; - GIOFlags flags; - gsize read; - - ret = gst_a2dp_sender_sink_bluetooth_recvmsg_fd(self); - if (ret < 0) - return FALSE; - - if (!self->stream) { - GST_ERROR_OBJECT(self, "Error while configuring device: " - "could not acquire audio socket"); - return FALSE; - } - - /* set stream socket to nonblock */ - GST_LOG_OBJECT(self, "setting stream socket to nonblock"); - flags = g_io_channel_get_flags(self->stream); - flags |= G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to nonblock: " - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to nonblock"); - } - - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - GST_LOG_OBJECT(self, "emptying stream pipe"); - while (1) { - err = g_io_channel_read(self->stream, data->buffer, - (gsize) data->link_mtu, - &read); - if (err != G_IO_ERROR_NONE || read <= 0) - break; - } - - /* set stream socket to block */ - GST_LOG_OBJECT(self, "setting stream socket to block"); - flags = g_io_channel_get_flags(self->stream); - flags &= ~G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to block:" - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to block"); - } - - memset(data->buffer, 0, sizeof(data->buffer)); - - return TRUE; -} - -static gboolean server_callback(GIOChannel *chan, - GIOCondition cond, gpointer data) -{ - GstA2dpSenderSink *sink; - - if (cond & G_IO_HUP || cond & G_IO_NVAL) - return FALSE; - else if (cond & G_IO_ERR) { - sink = GST_A2DP_SENDER_SINK(data); - GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); - } - - return TRUE; -} - -static GstStructure *gst_a2dp_sender_sink_parse_sbc_caps( - GstA2dpSenderSink *self, sbc_capabilities_t *sbc) -{ - GstStructure *structure; - GValue *value; - GValue *list; - gboolean mono, stereo; - - structure = gst_structure_empty_new("audio/x-sbc"); - value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); - - /* mode */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } else { - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "mode", list); - g_free(list); - list = NULL; - } - - /* subbands */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - value = g_value_init(value, G_TYPE_INT); - if (sbc->subbands & BT_A2DP_SUBBANDS_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - if (sbc->subbands & BT_A2DP_SUBBANDS_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "subbands", list); - g_free(list); - list = NULL; - } - - /* blocks */ - value = g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { - g_value_set_int(value, 16); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { - g_value_set_int(value, 12); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "blocks", list); - g_free(list); - list = NULL; - } - - /* allocation */ - g_value_init(value, G_TYPE_STRING); - list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } else { - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "allocation", list); - g_free(list); - list = NULL; - } - - /* rate */ - g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* bitpool */ - value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, - MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), - MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); - gst_structure_set_value(structure, "bitpool", value); - g_value_unset(value); - - /* channels */ - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - mono = FALSE; - stereo = FALSE; - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - } - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static GstStructure *gst_a2dp_sender_sink_parse_mpeg_caps( - GstA2dpSenderSink *self, mpeg_capabilities_t *mpeg) -{ - GstStructure *structure; - GValue *value; - GValue *list; - gboolean valid_layer = FALSE; - gboolean mono, stereo; - - GST_LOG_OBJECT(self, "parsing mpeg caps"); - - structure = gst_structure_empty_new("audio/mpeg"); - value = g_new0(GValue, 1); - g_value_init(value, G_TYPE_INT); - - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - gst_structure_set_value(structure, "mpegversion", list); - g_free(list); - - /* layer */ - GST_LOG_OBJECT(self, "setting mpeg layer"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->layer & BT_MPEG_LAYER_1) { - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_2) { - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_3) { - g_value_set_int(value, 3); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (list) { - gst_structure_set_value(structure, "layer", list); - g_free(list); - list = NULL; - } - - if (!valid_layer) { - gst_structure_free(structure); - g_free(value); - return NULL; - } - - /* rate */ - GST_LOG_OBJECT(self, "setting mpeg rate"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { - g_value_set_int(value, 24000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { - g_value_set_int(value, 22050); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* channels */ - GST_LOG_OBJECT(self, "setting mpeg channels"); - mono = FALSE; - stereo = FALSE; - if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static gboolean gst_a2dp_sender_sink_update_caps(GstA2dpSenderSink *self) -{ - sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; - mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; - GstStructure *sbc_structure; - GstStructure *mpeg_structure; - gchar *tmp; - - GST_LOG_OBJECT(self, "updating device caps"); - - sbc_structure = gst_a2dp_sender_sink_parse_sbc_caps(self, sbc); - mpeg_structure = gst_a2dp_sender_sink_parse_mpeg_caps(self, mpeg); - - if (self->dev_caps != NULL) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(sbc_structure, NULL); - if (mpeg_structure != NULL) - gst_caps_append_structure(self->dev_caps, mpeg_structure); - - tmp = gst_caps_to_string(self->dev_caps); - GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); - g_free(tmp); - - return TRUE; -} - -static gboolean gst_a2dp_sender_sink_get_capabilities(GstA2dpSenderSink *self) -{ - gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_getcapabilities_req *req = (void *) buf; - struct bt_getcapabilities_rsp *rsp = (void *) buf; - GIOError io_error; - - memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); - - req->h.msg_type = BT_GETCAPABILITIES_REQ; - if (self->device == NULL) - return FALSE; - strncpy(req->device, self->device, 18); - - io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while asking device caps"); - return FALSE; - } - - io_error = gst_a2dp_sender_sink_audioservice_expect(self, - &rsp->rsp_h.msg_h, BT_GETCAPABILITIES_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while getting device caps"); - return FALSE; - } - - if (rsp->rsp_h.posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", - strerror(rsp->rsp_h.posix_errno), - rsp->rsp_h.posix_errno); - return FALSE; - } - - memcpy(&self->data->caps, rsp, sizeof(*rsp)); - if (!gst_a2dp_sender_sink_update_caps(self)) { - GST_WARNING_OBJECT(self, "failed to update capabilities"); - return FALSE; - } - - return TRUE; -} - -static gint gst_a2dp_sender_sink_get_channel_mode(const gchar *mode) -{ - if (strcmp(mode, "stereo") == 0) - return BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(mode, "joint") == 0) - return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (strcmp(mode, "dual") == 0) - return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(mode, "mono") == 0) - return BT_A2DP_CHANNEL_MODE_MONO; - else - return -1; -} - -static void gst_a2dp_sender_sink_tag(const GstTagList *taglist, - const gchar* tag, gpointer user_data) -{ - gboolean crc; - gchar *channel_mode = NULL; - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(user_data); - - if (strcmp(tag, "has-crc") == 0) { - - if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { - GST_WARNING_OBJECT(self, "failed to get crc tag"); - self->mpeg_stream_changed = TRUE; - } - - gst_a2dp_sender_sink_set_crc(self, crc); - - } else if (strcmp(tag, "channel-mode") == 0) { - - if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { - GST_WARNING_OBJECT(self, - "failed to get channel-mode tag"); - self->mpeg_stream_changed = TRUE; - } - - self->channel_mode = gst_a2dp_sender_sink_get_channel_mode( - channel_mode); - if (self->channel_mode == -1) - GST_WARNING_OBJECT(self, "Received invalid channel " - "mode: %s", channel_mode); - g_free(channel_mode); - - } else - GST_DEBUG_OBJECT(self, "received unused tag: %s", tag); -} - -static gboolean gst_a2dp_sender_sink_event(GstBaseSink *basesink, - GstEvent *event) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - GstTagList *taglist = NULL; - - if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { - /* we check the tags, mp3 has tags that are importants and - * are outside caps */ - gst_event_parse_tag(event, &taglist); - gst_tag_list_foreach(taglist, gst_a2dp_sender_sink_tag, self); - } - - return TRUE; -} - -static gboolean gst_a2dp_sender_sink_start(GstBaseSink *basesink) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - gint sk; - gint err; - - GST_INFO_OBJECT(self, "start"); - - self->watch_id = 0; - - sk = bt_audio_service_open(); - if (sk <= 0) { - err = errno; - GST_ERROR_OBJECT(self, "Cannot open connection to bt " - "audio service: %s %d", strerror(err), err); - goto failed; - } - - self->server = g_io_channel_unix_new(sk); - self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | - G_IO_NVAL, server_callback, self); - - self->data = g_new0(struct bluetooth_data, 1); - memset(self->data, 0, sizeof(struct bluetooth_data)); - - self->stream = NULL; - self->stream_caps = NULL; - self->mp3_using_crc = -1; - self->channel_mode = -1; - self->mpeg_stream_changed = FALSE; - - if (!gst_a2dp_sender_sink_get_capabilities(self)) { - GST_ERROR_OBJECT(self, "failed to get capabilities " - "from device"); - goto failed; - } - - return TRUE; - -failed: - bt_audio_service_close(sk); - return FALSE; -} - -static gboolean gst_a2dp_sender_sink_stream_start(GstA2dpSenderSink *self) -{ - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_streamstart_req *req = (void *) buf; - struct bt_streamstart_rsp *rsp = (void *) buf; - struct bt_streamfd_ind *ind = (void*) buf; - GIOError io_error; - - GST_DEBUG_OBJECT(self, "stream start"); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_STREAMSTART_REQ; - - io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "start packet"); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream start packet sent"); - - io_error = gst_a2dp_sender_sink_audioservice_expect(self, - &rsp->rsp_h.msg_h, BT_STREAMSTART_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while stream " - "start confirmation"); - return FALSE; - } - - if (rsp->rsp_h.posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", - strerror(rsp->rsp_h.posix_errno), - rsp->rsp_h.posix_errno); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "stream started"); - - io_error = gst_a2dp_sender_sink_audioservice_expect(self, &ind->h, - BT_STREAMFD_IND); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving " - "stream filedescriptor"); - return FALSE; - } - - if (!gst_a2dp_sender_sink_conf_recv_stream_fd(self)) - return FALSE; - - return TRUE; -} - -static gboolean gst_a2dp_sender_sink_init_mp3_pkt_conf( - GstA2dpSenderSink *self, GstCaps *caps, - mpeg_capabilities_t *pkt) -{ - const GValue *value = NULL; - gint rate, layer; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - /* layer */ - value = gst_structure_get_value(structure, "layer"); - layer = g_value_get_int(value); - if (layer == 1) - pkt->layer = BT_MPEG_LAYER_1; - else if (layer == 2) - pkt->layer = BT_MPEG_LAYER_2; - else if (layer == 3) - pkt->layer = BT_MPEG_LAYER_3; - else { - GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer); - return FALSE; - } - - /* crc */ - if (self->mp3_using_crc != -1) - pkt->crc = self->mp3_using_crc; - else { - GST_ERROR_OBJECT(self, "No info about crc was received, " - " can't proceed"); - return FALSE; - } - - /* channel mode */ - if (self->channel_mode != -1) - pkt->channel_mode = self->channel_mode; - else { - GST_ERROR_OBJECT(self, "No info about channel mode " - "received, can't proceed"); - return FALSE; - } - - /* mpf - we will only use the mandatory one */ - pkt->mpf = 0; - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100; - else if (rate == 48000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000; - else if (rate == 32000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000; - else if (rate == 24000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000; - else if (rate == 22050) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050; - else if (rate == 16000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(self, "Invalid rate while setting caps"); - return FALSE; - } - - /* vbr - we always say its vbr, we don't have how to know it */ - pkt->bitrate = 0x8000; - - /* bitrate - we don't set anything, its vbr */ - /* FIXME - is this right? */ - - return TRUE; -} - -static gboolean gst_a2dp_sender_sink_configure(GstA2dpSenderSink *self, - GstCaps *caps) -{ - gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; - struct bt_setconfiguration_req *req = (void *) buf; - struct bt_setconfiguration_rsp *rsp = (void *) buf; - gboolean ret; - GIOError io_error; - gchar *temp; - GstStructure *structure; - - temp = gst_caps_to_string(caps); - GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); - g_free(temp); - - memset (req, 0, sizeof(buf)); - req->h.msg_type = BT_SETCONFIGURATION_REQ; - req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; - strncpy(req->device, self->device, 18); - structure = gst_caps_get_structure(caps, 0); - - if (gst_structure_has_name(structure, "audio/x-sbc")) - ret = gst_a2dp_sender_sink_init_pkt_conf(self, caps, - &req->sbc_capabilities); - else if (gst_structure_has_name(structure, "audio/mpeg")) - ret = gst_a2dp_sender_sink_init_mp3_pkt_conf(self, caps, - &req->mpeg_capabilities); - else - ret = FALSE; - - if (!ret) { - GST_ERROR_OBJECT(self, "Couldn't parse caps " - "to packet configuration"); - return FALSE; - } - - io_error = gst_a2dp_sender_sink_audioservice_send(self, &req->h); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error ocurred while sending " - "configurarion packet"); - return FALSE; - } - - GST_DEBUG_OBJECT(self, "configuration packet sent"); - - io_error = gst_a2dp_sender_sink_audioservice_expect(self, - &rsp->rsp_h.msg_h, BT_SETCONFIGURATION_RSP); - if (io_error != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while receiving device " - "confirmation"); - return FALSE; - } - - if (rsp->rsp_h.posix_errno != 0) { - GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : " - "%s(%d)", - strerror(rsp->rsp_h.posix_errno), - rsp->rsp_h.posix_errno); - return FALSE; - } - - self->data->link_mtu = rsp->link_mtu; - GST_DEBUG_OBJECT(self, "configuration set"); - - return TRUE; -} - -static GstFlowReturn gst_a2dp_sender_sink_preroll(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSenderSink *sink = GST_A2DP_SENDER_SINK(basesink); - gboolean ret; - - GST_A2DP_SENDER_SINK_MUTEX_LOCK(sink); - - ret = gst_a2dp_sender_sink_stream_start(sink); - - GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(sink); - - if (!ret) - return GST_FLOW_ERROR; - - return GST_FLOW_OK; -} - -static GstFlowReturn gst_a2dp_sender_sink_render(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - gsize ret; - GIOError err; - - err = g_io_channel_write(self->stream, (gchar*)GST_BUFFER_DATA(buffer), - (gsize)(GST_BUFFER_SIZE(buffer)), &ret); - - if (err != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error while writting to socket: %d %s", - errno, strerror(errno)); - return GST_FLOW_ERROR; - } - - return GST_FLOW_OK; -} - -static gboolean gst_a2dp_sender_sink_unlock(GstBaseSink *basesink) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - - if (self->stream != NULL) - g_io_channel_flush (self->stream, NULL); - - return TRUE; -} - -static GstFlowReturn gst_a2dp_sender_sink_buffer_alloc(GstBaseSink *basesink, - guint64 offset, guint size, GstCaps* caps, - GstBuffer **buf) -{ - GstA2dpSenderSink *self = GST_A2DP_SENDER_SINK(basesink); - - *buf = gst_buffer_new_and_alloc(size); - if (!(*buf)) { - GST_ERROR_OBJECT(self, "buffer allocation failed"); - return GST_FLOW_ERROR; - } - - gst_buffer_set_caps(*buf, caps); - - GST_BUFFER_OFFSET(*buf) = offset; - - return GST_FLOW_OK; -} - -static void gst_a2dp_sender_sink_class_init(GstA2dpSenderSinkClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - object_class->finalize = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_finalize); - object_class->set_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_get_property); - - basesink_class->start = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_start); - basesink_class->stop = GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_stop); - basesink_class->render = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_render); - basesink_class->preroll = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_preroll); - basesink_class->unlock = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_unlock); - basesink_class->event = GST_DEBUG_FUNCPTR( - gst_a2dp_sender_sink_event); - - basesink_class->buffer_alloc = - GST_DEBUG_FUNCPTR(gst_a2dp_sender_sink_buffer_alloc); - - g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device address", - NULL, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT(a2dp_sender_sink_debug, "a2dpsendersink", 0, - "A2DP sink element"); -} - -static void gst_a2dp_sender_sink_init(GstA2dpSenderSink *self, - GstA2dpSenderSinkClass *klass) -{ - self->device = NULL; - self->data = NULL; - - self->stream = NULL; - - self->dev_caps = NULL; - - self->sink_lock = g_mutex_new(); -} - -static GIOError gst_a2dp_sender_sink_audioservice_send( - GstA2dpSenderSink *self, - const bt_audio_msg_header_t *msg) -{ - GIOError error; - gsize written; - - error = g_io_channel_write(self->server, (const gchar*) msg, - BT_AUDIO_IPC_PACKET_SIZE, &written); - if (error != G_IO_ERROR_NONE) - GST_ERROR_OBJECT(self, "Error sending data to audio service:" - " %s(%d)", strerror(errno), errno); - - return error; -} - -static GIOError gst_a2dp_sender_sink_audioservice_recv( - GstA2dpSenderSink *self, - bt_audio_msg_header_t *inmsg) -{ - GIOError status; - gsize bytes_read; - const char *type; - - status = g_io_channel_read(self->server, (gchar*) inmsg, - BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); - if (status != G_IO_ERROR_NONE) { - GST_ERROR_OBJECT(self, "Error receiving data from " - "audio service"); - return status; - } - - type = bt_audio_strmsg(inmsg->msg_type); - if (!type) { - status = G_IO_ERROR_INVAL; - GST_ERROR_OBJECT(self, "Bogus message type %d " - "received from audio service", - inmsg->msg_type); - } - - return status; -} - -static GIOError gst_a2dp_sender_sink_audioservice_expect( - GstA2dpSenderSink *self, bt_audio_msg_header_t *outmsg, - int expected_type) -{ - GIOError status; - - status = gst_a2dp_sender_sink_audioservice_recv(self, outmsg); - if (status != G_IO_ERROR_NONE) - return status; - - if (outmsg->msg_type != expected_type) - status = G_IO_ERROR_INVAL; - - return status; -} - -gboolean gst_a2dp_sender_sink_plugin_init (GstPlugin * plugin) -{ - return gst_element_register (plugin, "a2dpsendersink", - GST_RANK_NONE, GST_TYPE_A2DP_SENDER_SINK); -} - - -/* public functions */ -GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink) -{ - if (sink->dev_caps == NULL) - return NULL; - - return gst_caps_copy(sink->dev_caps); -} - -gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *self, - GstCaps *caps) -{ - gboolean ret; - - GST_DEBUG_OBJECT(self, "setting device caps"); - GST_A2DP_SENDER_SINK_MUTEX_LOCK(self); - ret = gst_a2dp_sender_sink_configure(self, caps); - - if (self->stream_caps) - gst_caps_unref(self->stream_caps); - self->stream_caps = gst_caps_ref(caps); - - GST_A2DP_SENDER_SINK_MUTEX_UNLOCK(self); - - return ret; -} - -guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink) -{ - return sink->data->link_mtu; -} - -void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *self, const gchar* dev) -{ - if (self->device != NULL) - g_free(self->device); - - GST_LOG_OBJECT(self, "Setting device: %s", dev); - self->device = g_strdup(dev); -} - -gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *self) -{ - return g_strdup(self->device); -} - -void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc) -{ - gint new_crc; - - new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED; - - /* test if we already received a different crc */ - if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { - GST_ERROR_OBJECT(self, "crc changed during stream"); - /* FIXME test this, its not being used anywhere */ - self->mpeg_stream_changed = TRUE; - return; - } - self->mp3_using_crc = new_crc; - -} - -void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, - const gchar *mode) -{ - gint new_mode; - - new_mode = gst_a2dp_sender_sink_get_channel_mode(mode); - - if (self->channel_mode != -1 && new_mode != self->channel_mode) { - GST_ERROR_OBJECT(self, "channel mode changed during stream"); - self->mpeg_stream_changed = TRUE; - } - - self->channel_mode = new_mode; - if (self->channel_mode == -1) - GST_WARNING_OBJECT(self, "Received invalid channel " - "mode: %s", mode); -} - - diff --git a/audio/gsta2dpsendersink.h b/audio/gsta2dpsendersink.h deleted file mode 100644 index f23c86b2..00000000 --- a/audio/gsta2dpsendersink.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2007 Marcel Holtmann - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef __GST_A2DP_SENDER_SINK_H -#define __GST_A2DP_SENDER_SINK_H - -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_A2DP_SENDER_SINK \ - (gst_a2dp_sender_sink_get_type()) -#define GST_A2DP_SENDER_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SENDER_SINK,\ - GstA2dpSenderSink)) -#define GST_A2DP_SENDER_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SENDER_SINK,\ - GstA2dpSenderSinkClass)) -#define GST_IS_A2DP_SENDER_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SENDER_SINK)) -#define GST_IS_A2DP_SENDER_SINK_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SENDER_SINK)) - -typedef struct _GstA2dpSenderSink GstA2dpSenderSink; -typedef struct _GstA2dpSenderSinkClass GstA2dpSenderSinkClass; - -struct bluetooth_data; - -struct _GstA2dpSenderSink { - GstBaseSink sink; - - gchar *device; - GIOChannel *stream; - - struct bluetooth_data *data; - GIOChannel *server; - - /* mp3 stream data (outside caps data)*/ - gboolean mpeg_stream_changed; - gint mp3_using_crc; - gint channel_mode; - - /* stream connection data */ - GstCaps *stream_caps; - - GstCaps *dev_caps; - - GMutex *sink_lock; - - guint watch_id; -}; - -struct _GstA2dpSenderSinkClass { - GstBaseSinkClass parent_class; -}; - -GType gst_a2dp_sender_sink_get_type(void); - -GstCaps *gst_a2dp_sender_sink_get_device_caps(GstA2dpSenderSink *sink); -gboolean gst_a2dp_sender_sink_set_device_caps(GstA2dpSenderSink *sink, - GstCaps *caps); - -guint gst_a2dp_sender_sink_get_link_mtu(GstA2dpSenderSink *sink); - -void gst_a2dp_sender_sink_set_device(GstA2dpSenderSink *sink, - const gchar* device); - -gchar *gst_a2dp_sender_sink_get_device(GstA2dpSenderSink *sink); - -gboolean gst_a2dp_sender_sink_plugin_init(GstPlugin *plugin); - -void gst_a2dp_sender_sink_set_crc(GstA2dpSenderSink *self, gboolean crc); - -void gst_a2dp_sender_sink_set_channel_mode(GstA2dpSenderSink *self, - const gchar *mode); - - -G_END_DECLS - -#endif /* __GST_A2DP_SENDER_SINK_H */ diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 7c68d17f..a3f3655a 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -137,7 +137,7 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, switch (prop_id) { case PROP_DEVICE: if (self->sink != NULL) - gst_a2dp_sender_sink_set_device(self->sink, + gst_avdtp_sink_set_device(self->sink, g_value_get_string(value)); if (self->device != NULL) @@ -160,7 +160,7 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, switch (prop_id) { case PROP_DEVICE: if (self->sink != NULL) { - device = gst_a2dp_sender_sink_get_device(self->sink); + device = gst_avdtp_sink_get_device(self->sink); if (device != NULL) g_value_take_string(value, device); } @@ -229,15 +229,15 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, case GST_STATE_CHANGE_NULL_TO_READY: self->sink_is_in_bin = FALSE; - self->sink = GST_A2DP_SENDER_SINK(gst_element_factory_make( - "a2dpsendersink", "sendersink")); + self->sink = GST_AVDTP_SINK(gst_element_factory_make( + "avdtpsink", "avdtpsink")); if (self->sink == NULL) { - GST_WARNING_OBJECT(self, "failed to create a2dpsendersink"); + GST_WARNING_OBJECT(self, "failed to create avdtpsink"); return GST_STATE_CHANGE_FAILURE; } if (self->device != NULL) - gst_a2dp_sender_sink_set_device(self->sink, + gst_avdtp_sink_set_device(self->sink, self->device); ret = gst_element_set_state(GST_ELEMENT(self->sink), @@ -270,7 +270,7 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->sink))) GST_WARNING_OBJECT(self, "Failed to remove " - "a2dpsendersink from bin"); + "avdtpsink from bin"); } else if (self->sink != NULL) { gst_element_set_state(GST_ELEMENT(self->sink), GST_STATE_NULL); @@ -316,7 +316,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) static GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) { - return gst_a2dp_sender_sink_get_device_caps(self->sink); + return gst_avdtp_sink_get_device_caps(self->sink); } static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad) @@ -348,34 +348,34 @@ static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) GstElement *sink; if (self->sink == NULL) - sink = gst_element_factory_make("a2dpsendersink", "sendersink"); + sink = gst_element_factory_make("avdtpsink", "avdtosink"); else sink = GST_ELEMENT(self->sink); if (sink == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create a2dpsendersink"); + GST_ERROR_OBJECT(self, "Couldn't create avdtpsink"); return FALSE; } if (!gst_bin_add(GST_BIN(self), sink)) { - GST_ERROR_OBJECT(self, "failed to add a2dpsendersink " + GST_ERROR_OBJECT(self, "failed to add avdtpsink " "to the bin"); goto cleanup_and_fail; } if (gst_element_set_state(sink, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "a2dpsendersink failed to go to ready"); + GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready"); goto remove_element_and_fail; } if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " - "to a2dpsendersink"); + "to avdtpsink"); goto remove_element_and_fail; } - self->sink = GST_A2DP_SENDER_SINK(sink); + self->sink = GST_AVDTP_SINK(sink); self->sink_is_in_bin = TRUE; g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); @@ -471,11 +471,11 @@ static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, /* send directly the crc */ if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc)) - gst_a2dp_sender_sink_set_crc(self->sink, crc); + gst_avdtp_sink_set_crc(self->sink, crc); if (gst_tag_list_get_string(self->taglist, "channel-mode", &mode)) - gst_a2dp_sender_sink_set_channel_mode(self->sink, mode); + gst_avdtp_sink_set_channel_mode(self->sink, mode); capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); gst_pad_send_event(capsfilterpad, event); @@ -483,11 +483,11 @@ static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, g_free(mode); } - if (!gst_a2dp_sender_sink_set_device_caps(self->sink, caps)) + if (!gst_avdtp_sink_set_device_caps(self->sink, caps)) return FALSE; g_object_set(G_OBJECT(self->rtp), "mtu", - gst_a2dp_sender_sink_get_link_mtu(self->sink), NULL); + gst_avdtp_sink_get_link_mtu(self->sink), NULL); /* we forward our new segment here if we have one */ if (self->newseg_event) { diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 368f837a..26da4c47 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -23,7 +23,7 @@ #include #include -#include "gsta2dpsendersink.h" +#include "gstavdtpsink.h" G_BEGIN_DECLS @@ -45,7 +45,7 @@ struct _GstA2dpSink { GstBin bin; GstBaseRTPPayload *rtp; - GstA2dpSenderSink *sink; + GstAvdtpSink *sink; GstElement *capsfilter; gchar *device; diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c new file mode 100644 index 00000000..c956ecbd --- /dev/null +++ b/audio/gstavdtpsink.c @@ -0,0 +1,1355 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "ipc.h" +#include "rtp.h" +#include "gstsbcutil.h" + +#include "gstavdtpsink.h" + +GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug); +#define GST_CAT_DEFAULT avdtp_sink_debug + +#define BUFFER_SIZE 2048 +#define TEMPLATE_MAX_BITPOOL 64 +#define CRC_PROTECTED 1 +#define CRC_UNPROTECTED 0 + +#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \ + g_mutex_lock (s->sink_lock); \ + } G_STMT_END + +#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ + g_mutex_unlock (s->sink_lock); \ + } G_STMT_END + + +struct bluetooth_data { + struct bt_getcapabilities_rsp caps; /* Bluetooth device caps */ + guint link_mtu; + + gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ +}; + +#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) +#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) + +enum { + PROP_0, + PROP_DEVICE, +}; + +GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink, + GST_TYPE_BASE_SINK); + +static const GstElementDetails avdtp_sink_details = + GST_ELEMENT_DETAILS("Bluetooth AVDTP sink", + "Sink/Audio", + "Plays audio to an A2DP device", + "Marcel Holtmann "); + +static GstStaticPadTemplate avdtp_sink_factory = + GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS("application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"SBC\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " + GST_RTP_PAYLOAD_MPA_STRING ", " + "clock-rate = (int) 90000; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " + GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"MPA\"" + )); + +static GIOError gst_avdtp_sink_audioservice_send(GstAvdtpSink *self, + const bt_audio_msg_header_t *msg); +static GIOError gst_avdtp_sink_audioservice_expect( + GstAvdtpSink *self, + bt_audio_msg_header_t *outmsg, + int expected_type); + + +static void gst_avdtp_sink_base_init(gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); + + gst_element_class_add_pad_template(element_class, + gst_static_pad_template_get(&avdtp_sink_factory)); + + gst_element_class_set_details(element_class, &avdtp_sink_details); +} + +static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + + GST_INFO_OBJECT(self, "stop"); + + if (self->watch_id != 0) { + g_source_remove(self->watch_id); + self->watch_id = 0; + } + + if (self->server) { + bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); + g_io_channel_unref(self->server); + self->server = NULL; + } + + if (self->stream) { + g_io_channel_flush(self->stream, NULL); + g_io_channel_close(self->stream); + g_io_channel_unref(self->stream); + self->stream = NULL; + } + + if (self->data) { + g_free(self->data); + self->data = NULL; + } + + if (self->stream_caps) { + gst_caps_unref(self->stream_caps); + self->stream_caps = NULL; + } + + if (self->dev_caps) { + gst_caps_unref(self->dev_caps); + self->dev_caps = NULL; + } + + return TRUE; +} + +static void gst_avdtp_sink_finalize(GObject *object) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(object); + + if (self->data) + gst_avdtp_sink_stop(GST_BASE_SINK(self)); + + if (self->device) + g_free(self->device); + + g_mutex_free(self->sink_lock); + + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static void gst_avdtp_sink_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GstAvdtpSink *sink = GST_AVDTP_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + if (sink->device) + g_free(sink->device); + sink->device = g_value_dup_string(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gst_avdtp_sink_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstAvdtpSink *sink = GST_AVDTP_SINK(object); + + switch (prop_id) { + case PROP_DEVICE: + g_value_set_string(value, sink->device); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gint gst_avdtp_sink_bluetooth_recvmsg_fd(GstAvdtpSink *sink) +{ + int err, ret; + + ret = bt_audio_service_get_data_fd( + g_io_channel_unix_get_fd(sink->server)); + + if (ret < 0) { + err = errno; + GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", + strerror(err), err); + return -err; + } + + sink->stream = g_io_channel_unix_new(ret); + GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); + + return 0; +} + +static gboolean gst_avdtp_sink_init_pkt_conf(GstAvdtpSink *sink, + GstCaps *caps, + sbc_capabilities_t *pkt) +{ + sbc_capabilities_t *cfg = &sink->data->caps.sbc_capabilities; + const GValue *value = NULL; + const char *pref, *name; + gint rate, subbands, blocks; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + name = gst_structure_get_name(structure); + /* FIXME only sbc supported here, should suport mp3 */ + if (!(IS_SBC(name))) { + GST_ERROR_OBJECT(sink, "Unsupported format %s", name); + return FALSE; + } + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + cfg->frequency = BT_SBC_SAMPLING_FREQ_44100; + else if (rate == 48000) + cfg->frequency = BT_SBC_SAMPLING_FREQ_48000; + else if (rate == 32000) + cfg->frequency = BT_SBC_SAMPLING_FREQ_32000; + else if (rate == 16000) + cfg->frequency = BT_SBC_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); + return FALSE; + } + + value = gst_structure_get_value(structure, "mode"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; + else if (strcmp(pref, "mono") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + else if (strcmp(pref, "dual") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(pref, "stereo") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(pref, "joint") == 0) + cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else { + GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "allocation"); + pref = g_value_get_string(value); + if (strcmp(pref, "auto") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; + else if (strcmp(pref, "loudness") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + else if (strcmp(pref, "snr") == 0) + cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; + else { + GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); + return FALSE; + } + + value = gst_structure_get_value(structure, "subbands"); + subbands = g_value_get_int(value); + if (subbands == 8) + cfg->subbands = BT_A2DP_SUBBANDS_8; + else if (subbands == 4) + cfg->subbands = BT_A2DP_SUBBANDS_4; + else { + GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); + return FALSE; + } + + value = gst_structure_get_value(structure, "blocks"); + blocks = g_value_get_int(value); + if (blocks == 16) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (blocks == 12) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (blocks == 8) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (blocks == 4) + cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); + return FALSE; + } + + value = gst_structure_get_value(structure, "bitpool"); + cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); + + memcpy(pkt, cfg, sizeof(*pkt)); + + return TRUE; +} + +static gboolean gst_avdtp_sink_conf_recv_stream_fd( + GstAvdtpSink *self) +{ + struct bluetooth_data *data = self->data; + gint ret; + GIOError err; + GError *gerr = NULL; + GIOStatus status; + GIOFlags flags; + gsize read; + + ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self); + if (ret < 0) + return FALSE; + + if (!self->stream) { + GST_ERROR_OBJECT(self, "Error while configuring device: " + "could not acquire audio socket"); + return FALSE; + } + + /* set stream socket to nonblock */ + GST_LOG_OBJECT(self, "setting stream socket to nonblock"); + flags = g_io_channel_get_flags(self->stream); + flags |= G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to nonblock: " + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to nonblock"); + } + + /* It is possible there is some outstanding + data in the pipe - we have to empty it */ + GST_LOG_OBJECT(self, "emptying stream pipe"); + while (1) { + err = g_io_channel_read(self->stream, data->buffer, + (gsize) data->link_mtu, + &read); + if (err != G_IO_ERROR_NONE || read <= 0) + break; + } + + /* set stream socket to block */ + GST_LOG_OBJECT(self, "setting stream socket to block"); + flags = g_io_channel_get_flags(self->stream); + flags &= ~G_IO_FLAG_NONBLOCK; + status = g_io_channel_set_flags(self->stream, flags, &gerr); + if (status != G_IO_STATUS_NORMAL) { + if (gerr) + GST_WARNING_OBJECT(self, "Error while " + "setting server socket to block:" + "%s", gerr->message); + else + GST_WARNING_OBJECT(self, "Error while " + "setting server " + "socket to block"); + } + + memset(data->buffer, 0, sizeof(data->buffer)); + + return TRUE; +} + +static gboolean server_callback(GIOChannel *chan, + GIOCondition cond, gpointer data) +{ + GstAvdtpSink *sink; + + if (cond & G_IO_HUP || cond & G_IO_NVAL) + return FALSE; + else if (cond & G_IO_ERR) { + sink = GST_AVDTP_SINK(data); + GST_WARNING_OBJECT(sink, "Untreated callback G_IO_ERR"); + } + + return TRUE; +} + +static GstStructure *gst_avdtp_sink_parse_sbc_caps( + GstAvdtpSink *self, sbc_capabilities_t *sbc) +{ + GstStructure *structure; + GValue *value; + GValue *list; + gboolean mono, stereo; + + structure = gst_structure_empty_new("audio/x-sbc"); + value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); + + /* mode */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { + g_value_set_static_string(value, "stereo"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { + g_value_set_static_string(value, "dual"); + gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "mode", list); + g_free(list); + list = NULL; + } + + /* subbands */ + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + value = g_value_init(value, G_TYPE_INT); + if (sbc->subbands & BT_A2DP_SUBBANDS_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + if (sbc->subbands & BT_A2DP_SUBBANDS_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "subbands", list); + g_free(list); + list = NULL; + } + + /* blocks */ + value = g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { + g_value_set_int(value, 16); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { + g_value_set_int(value, 12); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { + g_value_set_int(value, 8); + gst_value_list_prepend_value(list, value); + } + if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { + g_value_set_int(value, 4); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "blocks", list); + g_free(list); + list = NULL; + } + + /* allocation */ + g_value_init(value, G_TYPE_STRING); + list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); + if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } else { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { + g_value_set_static_string(value, "loudness"); + gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { + g_value_set_static_string(value, "snr"); + gst_value_list_prepend_value(list, value); + } + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "allocation", list); + g_free(list); + list = NULL; + } + + /* rate */ + g_value_init(value, G_TYPE_INT); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* bitpool */ + value = g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, + MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), + MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); + gst_structure_set_value(structure, "bitpool", value); + g_value_unset(value); + + /* channels */ + if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + mono = FALSE; + stereo = FALSE; + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + return structure; +} + +static GstStructure *gst_avdtp_sink_parse_mpeg_caps( + GstAvdtpSink *self, mpeg_capabilities_t *mpeg) +{ + GstStructure *structure; + GValue *value; + GValue *list; + gboolean valid_layer = FALSE; + gboolean mono, stereo; + + GST_LOG_OBJECT(self, "parsing mpeg caps"); + + structure = gst_structure_empty_new("audio/mpeg"); + value = g_new0(GValue, 1); + g_value_init(value, G_TYPE_INT); + + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + gst_structure_set_value(structure, "mpegversion", list); + g_free(list); + + /* layer */ + GST_LOG_OBJECT(self, "setting mpeg layer"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->layer & BT_MPEG_LAYER_1) { + g_value_set_int(value, 1); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_2) { + g_value_set_int(value, 2); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (mpeg->layer & BT_MPEG_LAYER_3) { + g_value_set_int(value, 3); + gst_value_list_prepend_value(list, value); + valid_layer = TRUE; + } + if (list) { + gst_structure_set_value(structure, "layer", list); + g_free(list); + list = NULL; + } + + if (!valid_layer) { + gst_structure_free(structure); + g_free(value); + return NULL; + } + + /* rate */ + GST_LOG_OBJECT(self, "setting mpeg rate"); + list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { + g_value_set_int(value, 48000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { + g_value_set_int(value, 44100); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { + g_value_set_int(value, 32000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { + g_value_set_int(value, 24000); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { + g_value_set_int(value, 22050); + gst_value_list_prepend_value(list, value); + } + if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { + g_value_set_int(value, 16000); + gst_value_list_prepend_value(list, value); + } + g_value_unset(value); + if (list) { + gst_structure_set_value(structure, "rate", list); + g_free(list); + list = NULL; + } + + /* channels */ + GST_LOG_OBJECT(self, "setting mpeg channels"); + mono = FALSE; + stereo = FALSE; + if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (mpeg->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { + g_value_init(value, GST_TYPE_INT_RANGE); + gst_value_set_int_range(value, 1, 2); + } else { + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); + } + } + gst_structure_set_value(structure, "channels", value); + g_free(value); + + return structure; +} + +static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self) +{ + sbc_capabilities_t *sbc = &self->data->caps.sbc_capabilities; + mpeg_capabilities_t *mpeg = &self->data->caps.mpeg_capabilities; + GstStructure *sbc_structure; + GstStructure *mpeg_structure; + gchar *tmp; + + GST_LOG_OBJECT(self, "updating device caps"); + + sbc_structure = gst_avdtp_sink_parse_sbc_caps(self, sbc); + mpeg_structure = gst_avdtp_sink_parse_mpeg_caps(self, mpeg); + + if (self->dev_caps != NULL) + gst_caps_unref(self->dev_caps); + self->dev_caps = gst_caps_new_full(sbc_structure, NULL); + if (mpeg_structure != NULL) + gst_caps_append_structure(self->dev_caps, mpeg_structure); + + tmp = gst_caps_to_string(self->dev_caps); + GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); + g_free(tmp); + + return TRUE; +} + +static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self) +{ + gchar *buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_getcapabilities_req *req = (void *) buf; + struct bt_getcapabilities_rsp *rsp = (void *) buf; + GIOError io_error; + + memset(req, 0, BT_AUDIO_IPC_PACKET_SIZE); + + req->h.msg_type = BT_GETCAPABILITIES_REQ; + if (self->device == NULL) + return FALSE; + strncpy(req->device, self->device, 18); + + io_error = gst_avdtp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while asking device caps"); + return FALSE; + } + + io_error = gst_avdtp_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_GETCAPABILITIES_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while getting device caps"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_GETCAPABILITIES failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + memcpy(&self->data->caps, rsp, sizeof(*rsp)); + if (!gst_avdtp_sink_update_caps(self)) { + GST_WARNING_OBJECT(self, "failed to update capabilities"); + return FALSE; + } + + return TRUE; +} + +static gint gst_avdtp_sink_get_channel_mode(const gchar *mode) +{ + if (strcmp(mode, "stereo") == 0) + return BT_A2DP_CHANNEL_MODE_STEREO; + else if (strcmp(mode, "joint") == 0) + return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (strcmp(mode, "dual") == 0) + return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (strcmp(mode, "mono") == 0) + return BT_A2DP_CHANNEL_MODE_MONO; + else + return -1; +} + +static void gst_avdtp_sink_tag(const GstTagList *taglist, + const gchar* tag, gpointer user_data) +{ + gboolean crc; + gchar *channel_mode = NULL; + GstAvdtpSink *self = GST_AVDTP_SINK(user_data); + + if (strcmp(tag, "has-crc") == 0) { + + if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { + GST_WARNING_OBJECT(self, "failed to get crc tag"); + self->mpeg_stream_changed = TRUE; + } + + gst_avdtp_sink_set_crc(self, crc); + + } else if (strcmp(tag, "channel-mode") == 0) { + + if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { + GST_WARNING_OBJECT(self, + "failed to get channel-mode tag"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = gst_avdtp_sink_get_channel_mode( + channel_mode); + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", channel_mode); + g_free(channel_mode); + + } else + GST_DEBUG_OBJECT(self, "received unused tag: %s", tag); +} + +static gboolean gst_avdtp_sink_event(GstBaseSink *basesink, + GstEvent *event) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + GstTagList *taglist = NULL; + + if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + /* we check the tags, mp3 has tags that are importants and + * are outside caps */ + gst_event_parse_tag(event, &taglist); + gst_tag_list_foreach(taglist, gst_avdtp_sink_tag, self); + } + + return TRUE; +} + +static gboolean gst_avdtp_sink_start(GstBaseSink *basesink) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + gint sk; + gint err; + + GST_INFO_OBJECT(self, "start"); + + self->watch_id = 0; + + sk = bt_audio_service_open(); + if (sk <= 0) { + err = errno; + GST_ERROR_OBJECT(self, "Cannot open connection to bt " + "audio service: %s %d", strerror(err), err); + goto failed; + } + + self->server = g_io_channel_unix_new(sk); + self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | + G_IO_NVAL, server_callback, self); + + self->data = g_new0(struct bluetooth_data, 1); + memset(self->data, 0, sizeof(struct bluetooth_data)); + + self->stream = NULL; + self->stream_caps = NULL; + self->mp3_using_crc = -1; + self->channel_mode = -1; + self->mpeg_stream_changed = FALSE; + + if (!gst_avdtp_sink_get_capabilities(self)) { + GST_ERROR_OBJECT(self, "failed to get capabilities " + "from device"); + goto failed; + } + + return TRUE; + +failed: + bt_audio_service_close(sk); + return FALSE; +} + +static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_streamstart_req *req = (void *) buf; + struct bt_streamstart_rsp *rsp = (void *) buf; + struct bt_streamfd_ind *ind = (void*) buf; + GIOError io_error; + + GST_DEBUG_OBJECT(self, "stream start"); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_STREAMSTART_REQ; + + io_error = gst_avdtp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "start packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream start packet sent"); + + io_error = gst_avdtp_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_STREAMSTART_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while stream " + "start confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_STREAMSTART_RSP failed : %s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "stream started"); + + io_error = gst_avdtp_sink_audioservice_expect(self, &ind->h, + BT_STREAMFD_IND); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving " + "stream filedescriptor"); + return FALSE; + } + + if (!gst_avdtp_sink_conf_recv_stream_fd(self)) + return FALSE; + + return TRUE; +} + +static gboolean gst_avdtp_sink_init_mp3_pkt_conf( + GstAvdtpSink *self, GstCaps *caps, + mpeg_capabilities_t *pkt) +{ + const GValue *value = NULL; + gint rate, layer; + GstStructure *structure = gst_caps_get_structure(caps, 0); + + /* layer */ + value = gst_structure_get_value(structure, "layer"); + layer = g_value_get_int(value); + if (layer == 1) + pkt->layer = BT_MPEG_LAYER_1; + else if (layer == 2) + pkt->layer = BT_MPEG_LAYER_2; + else if (layer == 3) + pkt->layer = BT_MPEG_LAYER_3; + else { + GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer); + return FALSE; + } + + /* crc */ + if (self->mp3_using_crc != -1) + pkt->crc = self->mp3_using_crc; + else { + GST_ERROR_OBJECT(self, "No info about crc was received, " + " can't proceed"); + return FALSE; + } + + /* channel mode */ + if (self->channel_mode != -1) + pkt->channel_mode = self->channel_mode; + else { + GST_ERROR_OBJECT(self, "No info about channel mode " + "received, can't proceed"); + return FALSE; + } + + /* mpf - we will only use the mandatory one */ + pkt->mpf = 0; + + value = gst_structure_get_value(structure, "rate"); + rate = g_value_get_int(value); + if (rate == 44100) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100; + else if (rate == 48000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000; + else if (rate == 32000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000; + else if (rate == 24000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000; + else if (rate == 22050) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050; + else if (rate == 16000) + pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000; + else { + GST_ERROR_OBJECT(self, "Invalid rate while setting caps"); + return FALSE; + } + + /* vbr - we always say its vbr, we don't have how to know it */ + pkt->bitrate = 0x8000; + + /* bitrate - we don't set anything, its vbr */ + /* FIXME - is this right? */ + + return TRUE; +} + +static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self, + GstCaps *caps) +{ + gchar buf[BT_AUDIO_IPC_PACKET_SIZE]; + struct bt_setconfiguration_req *req = (void *) buf; + struct bt_setconfiguration_rsp *rsp = (void *) buf; + gboolean ret; + GIOError io_error; + gchar *temp; + GstStructure *structure; + + temp = gst_caps_to_string(caps); + GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); + g_free(temp); + + memset (req, 0, sizeof(buf)); + req->h.msg_type = BT_SETCONFIGURATION_REQ; + req->access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE; + strncpy(req->device, self->device, 18); + structure = gst_caps_get_structure(caps, 0); + + if (gst_structure_has_name(structure, "audio/x-sbc")) + ret = gst_avdtp_sink_init_pkt_conf(self, caps, + &req->sbc_capabilities); + else if (gst_structure_has_name(structure, "audio/mpeg")) + ret = gst_avdtp_sink_init_mp3_pkt_conf(self, caps, + &req->mpeg_capabilities); + else + ret = FALSE; + + if (!ret) { + GST_ERROR_OBJECT(self, "Couldn't parse caps " + "to packet configuration"); + return FALSE; + } + + io_error = gst_avdtp_sink_audioservice_send(self, &req->h); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error ocurred while sending " + "configurarion packet"); + return FALSE; + } + + GST_DEBUG_OBJECT(self, "configuration packet sent"); + + io_error = gst_avdtp_sink_audioservice_expect(self, + &rsp->rsp_h.msg_h, BT_SETCONFIGURATION_RSP); + if (io_error != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while receiving device " + "confirmation"); + return FALSE; + } + + if (rsp->rsp_h.posix_errno != 0) { + GST_ERROR_OBJECT(self, "BT_SETCONFIGURATION_RSP failed : " + "%s(%d)", + strerror(rsp->rsp_h.posix_errno), + rsp->rsp_h.posix_errno); + return FALSE; + } + + self->data->link_mtu = rsp->link_mtu; + GST_DEBUG_OBJECT(self, "configuration set"); + + return TRUE; +} + +static GstFlowReturn gst_avdtp_sink_preroll(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstAvdtpSink *sink = GST_AVDTP_SINK(basesink); + gboolean ret; + + GST_AVDTP_SINK_MUTEX_LOCK(sink); + + ret = gst_avdtp_sink_stream_start(sink); + + GST_AVDTP_SINK_MUTEX_UNLOCK(sink); + + if (!ret) + return GST_FLOW_ERROR; + + return GST_FLOW_OK; +} + +static GstFlowReturn gst_avdtp_sink_render(GstBaseSink *basesink, + GstBuffer *buffer) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + gsize ret; + GIOError err; + + err = g_io_channel_write(self->stream, (gchar*)GST_BUFFER_DATA(buffer), + (gsize)(GST_BUFFER_SIZE(buffer)), &ret); + + if (err != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error while writting to socket: %d %s", + errno, strerror(errno)); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static gboolean gst_avdtp_sink_unlock(GstBaseSink *basesink) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + + if (self->stream != NULL) + g_io_channel_flush (self->stream, NULL); + + return TRUE; +} + +static GstFlowReturn gst_avdtp_sink_buffer_alloc(GstBaseSink *basesink, + guint64 offset, guint size, GstCaps* caps, + GstBuffer **buf) +{ + GstAvdtpSink *self = GST_AVDTP_SINK(basesink); + + *buf = gst_buffer_new_and_alloc(size); + if (!(*buf)) { + GST_ERROR_OBJECT(self, "buffer allocation failed"); + return GST_FLOW_ERROR; + } + + gst_buffer_set_caps(*buf, caps); + + GST_BUFFER_OFFSET(*buf) = offset; + + return GST_FLOW_OK; +} + +static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + object_class->finalize = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_finalize); + object_class->set_property = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_set_property); + object_class->get_property = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_get_property); + + basesink_class->start = GST_DEBUG_FUNCPTR(gst_avdtp_sink_start); + basesink_class->stop = GST_DEBUG_FUNCPTR(gst_avdtp_sink_stop); + basesink_class->render = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_render); + basesink_class->preroll = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_preroll); + basesink_class->unlock = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_unlock); + basesink_class->event = GST_DEBUG_FUNCPTR( + gst_avdtp_sink_event); + + basesink_class->buffer_alloc = + GST_DEBUG_FUNCPTR(gst_avdtp_sink_buffer_alloc); + + g_object_class_install_property(object_class, PROP_DEVICE, + g_param_spec_string("device", "Device", + "Bluetooth remote device address", + NULL, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "a2dpsendersink", 0, + "A2DP sink element"); +} + +static void gst_avdtp_sink_init(GstAvdtpSink *self, + GstAvdtpSinkClass *klass) +{ + self->device = NULL; + self->data = NULL; + + self->stream = NULL; + + self->dev_caps = NULL; + + self->sink_lock = g_mutex_new(); +} + +static GIOError gst_avdtp_sink_audioservice_send( + GstAvdtpSink *self, + const bt_audio_msg_header_t *msg) +{ + GIOError error; + gsize written; + + error = g_io_channel_write(self->server, (const gchar*) msg, + BT_AUDIO_IPC_PACKET_SIZE, &written); + if (error != G_IO_ERROR_NONE) + GST_ERROR_OBJECT(self, "Error sending data to audio service:" + " %s(%d)", strerror(errno), errno); + + return error; +} + +static GIOError gst_avdtp_sink_audioservice_recv( + GstAvdtpSink *self, + bt_audio_msg_header_t *inmsg) +{ + GIOError status; + gsize bytes_read; + const char *type; + + status = g_io_channel_read(self->server, (gchar*) inmsg, + BT_AUDIO_IPC_PACKET_SIZE, &bytes_read); + if (status != G_IO_ERROR_NONE) { + GST_ERROR_OBJECT(self, "Error receiving data from " + "audio service"); + return status; + } + + type = bt_audio_strmsg(inmsg->msg_type); + if (!type) { + status = G_IO_ERROR_INVAL; + GST_ERROR_OBJECT(self, "Bogus message type %d " + "received from audio service", + inmsg->msg_type); + } + + return status; +} + +static GIOError gst_avdtp_sink_audioservice_expect( + GstAvdtpSink *self, bt_audio_msg_header_t *outmsg, + int expected_type) +{ + GIOError status; + + status = gst_avdtp_sink_audioservice_recv(self, outmsg); + if (status != G_IO_ERROR_NONE) + return status; + + if (outmsg->msg_type != expected_type) + status = G_IO_ERROR_INVAL; + + return status; +} + +gboolean gst_avdtp_sink_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "avdtpsink", + GST_RANK_NONE, GST_TYPE_AVDTP_SINK); +} + + +/* public functions */ +GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink) +{ + if (sink->dev_caps == NULL) + return NULL; + + return gst_caps_copy(sink->dev_caps); +} + +gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *self, + GstCaps *caps) +{ + gboolean ret; + + GST_DEBUG_OBJECT(self, "setting device caps"); + GST_AVDTP_SINK_MUTEX_LOCK(self); + ret = gst_avdtp_sink_configure(self, caps); + + if (self->stream_caps) + gst_caps_unref(self->stream_caps); + self->stream_caps = gst_caps_ref(caps); + + GST_AVDTP_SINK_MUTEX_UNLOCK(self); + + return ret; +} + +guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink) +{ + return sink->data->link_mtu; +} + +void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar* dev) +{ + if (self->device != NULL) + g_free(self->device); + + GST_LOG_OBJECT(self, "Setting device: %s", dev); + self->device = g_strdup(dev); +} + +gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self) +{ + return g_strdup(self->device); +} + +void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc) +{ + gint new_crc; + + new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED; + + /* test if we already received a different crc */ + if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { + GST_ERROR_OBJECT(self, "crc changed during stream"); + /* FIXME test this, its not being used anywhere */ + self->mpeg_stream_changed = TRUE; + return; + } + self->mp3_using_crc = new_crc; + +} + +void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, + const gchar *mode) +{ + gint new_mode; + + new_mode = gst_avdtp_sink_get_channel_mode(mode); + + if (self->channel_mode != -1 && new_mode != self->channel_mode) { + GST_ERROR_OBJECT(self, "channel mode changed during stream"); + self->mpeg_stream_changed = TRUE; + } + + self->channel_mode = new_mode; + if (self->channel_mode == -1) + GST_WARNING_OBJECT(self, "Received invalid channel " + "mode: %s", mode); +} + + diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h new file mode 100644 index 00000000..d0380829 --- /dev/null +++ b/audio/gstavdtpsink.h @@ -0,0 +1,101 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2007 Marcel Holtmann + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_AVDTP_SINK_H +#define __GST_AVDTP_SINK_H + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AVDTP_SINK \ + (gst_avdtp_sink_get_type()) +#define GST_AVDTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVDTP_SINK,\ + GstAvdtpSink)) +#define GST_AVDTP_SINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVDTP_SINK,\ + GstAvdtpSinkClass)) +#define GST_IS_AVDTP_SINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SINK)) +#define GST_IS_AVDTP_SINK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SINK)) + +typedef struct _GstAvdtpSink GstAvdtpSink; +typedef struct _GstAvdtpSinkClass GstAvdtpSinkClass; + +struct bluetooth_data; + +struct _GstAvdtpSink { + GstBaseSink sink; + + gchar *device; + GIOChannel *stream; + + struct bluetooth_data *data; + GIOChannel *server; + + /* mp3 stream data (outside caps data)*/ + gboolean mpeg_stream_changed; + gint mp3_using_crc; + gint channel_mode; + + /* stream connection data */ + GstCaps *stream_caps; + + GstCaps *dev_caps; + + GMutex *sink_lock; + + guint watch_id; +}; + +struct _GstAvdtpSinkClass { + GstBaseSinkClass parent_class; +}; + +GType gst_avdtp_sink_get_type(void); + +GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink); +gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *sink, + GstCaps *caps); + +guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink); + +void gst_avdtp_sink_set_device(GstAvdtpSink *sink, + const gchar* device); + +gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink); + +gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin); + +void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc); + +void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, + const gchar *mode); + + +G_END_DECLS + +#endif /* __GST_AVDTP_SINK_H */ diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index eaab23d2..4cf872ce 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -33,7 +33,7 @@ #include "gstsbcenc.h" #include "gstsbcdec.h" #include "gstsbcparse.h" -#include "gsta2dpsendersink.h" +#include "gstavdtpsink.h" #include "gsta2dpsink.h" #include "gstrtpsbcpay.h" @@ -85,7 +85,7 @@ static gboolean plugin_init(GstPlugin *plugin) if (!gst_sbc_parse_plugin_init(plugin)) return FALSE; - if (!gst_a2dp_sender_sink_plugin_init(plugin)) + if (!gst_avdtp_sink_plugin_init(plugin)) return FALSE; if (!gst_a2dp_sink_plugin_init(plugin)) -- cgit From 1f9f22f64d5e7c66c6e2d20452f6e5918d0020fb Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 23 Jan 2008 19:17:33 +0000 Subject: Fix use of gstreamer plugin with rhythmbox and banshee and rtp timestamps. --- audio/gsta2dpsink.c | 93 ++++++++++++++++++++++++++++++++++++++++++++-------- audio/gsta2dpsink.h | 11 +++++++ audio/gstrtpsbcpay.c | 6 ++-- audio/gstrtpsbcpay.h | 1 + 4 files changed, 96 insertions(+), 15 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index a3f3655a..a2b3286c 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -68,6 +68,17 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event); static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); +static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self); +static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self); + +static void gst_a2dp_sink_finalize(GObject *obj) +{ + GstA2dpSink *self = GST_A2DP_SINK(obj); + + g_mutex_free(self->cb_mutex); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} /* * Helper function to create elements, add to the bin and link it @@ -93,9 +104,9 @@ static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, goto cleanup_and_fail; } - if (gst_element_set_state(element, GST_STATE_READY) == + if (gst_element_set_state(element, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "%s failed to go to ready", + GST_ERROR_OBJECT(self, "%s failed to go to playing", elementname); goto remove_element_and_fail; } @@ -224,6 +235,8 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: self->taglist = gst_tag_list_new(); + + gst_a2dp_sink_init_fakesink(self); break; case GST_STATE_CHANGE_NULL_TO_READY: @@ -263,6 +276,7 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_event_unref(self->newseg_event); self->newseg_event = NULL; } + gst_a2dp_sink_remove_fakesink(self); break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -280,7 +294,6 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, self->sink = NULL; gst_a2dp_sink_remove_dynamic_elements(self); - break; default: @@ -302,6 +315,9 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) object_class->get_property = GST_DEBUG_FUNCPTR( gst_a2dp_sink_get_property); + object_class->finalize = GST_DEBUG_FUNCPTR( + gst_a2dp_sink_finalize); + element_class->change_state = GST_DEBUG_FUNCPTR( gst_a2dp_sink_change_state); @@ -314,7 +330,7 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) "A2DP sink element"); } -static GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) +GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) { return gst_avdtp_sink_get_device_caps(self->sink); } @@ -343,12 +359,16 @@ static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad) return caps; } -static gboolean gst_a2dp_sink_init_sender_sink(GstA2dpSink *self) +static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self) { GstElement *sink; + /* check if we don't need a new sink */ + if (self->sink_is_in_bin) + return TRUE; + if (self->sink == NULL) - sink = gst_element_factory_make("avdtpsink", "avdtosink"); + sink = gst_element_factory_make("avdtpsink", "avdtpsink"); else sink = GST_ELEMENT(self->sink); @@ -399,6 +419,10 @@ static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) { GstElement *rtppay; + /* if we already have a rtp, we don't need a new one */ + if (self->rtp != NULL) + return TRUE; + rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp", self->capsfilter); if (rtppay == NULL) @@ -416,13 +440,16 @@ static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) { GstElement *rtppay; - GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); + /* check if we don't need a new rtp */ + if (self->rtp) + return TRUE; + GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); /* if capsfilter is not created then we can't have our rtp element */ if (self->capsfilter == NULL) return FALSE; - rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", + rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", self->capsfilter); if (rtppay == NULL) return FALSE; @@ -445,6 +472,9 @@ static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, structure = gst_caps_get_structure(caps, 0); + /* before everything we need to remove fakesink */ + gst_a2dp_sink_remove_fakesink(self); + /* first, we need to create our rtp payloader */ if (gst_structure_has_name(structure, "audio/x-sbc")) { GST_LOG_OBJECT(self, "sbc media received"); @@ -459,7 +489,7 @@ static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, return FALSE; } - if (!gst_a2dp_sink_init_sender_sink(self)) + if (!gst_a2dp_sink_init_avdtp_sink(self)) return FALSE; /* check if we should push the taglist FIXME should we push this? @@ -518,18 +548,18 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) { GstA2dpSink *self; GstTagList *taglist = NULL; + GstObject *parent; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); + parent = gst_element_get_parent(GST_ELEMENT(self->sink)); if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT && - gst_element_get_parent(GST_ELEMENT(self->sink)) != - GST_OBJECT_CAST(self)) { + parent != GST_OBJECT_CAST(self)) { if (self->newseg_event != NULL) gst_event_unref(self->newseg_event); self->newseg_event = gst_event_ref(event); } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && - gst_element_get_parent(GST_ELEMENT(self->sink)) != - GST_OBJECT_CAST(self)) { + parent != GST_OBJECT_CAST(self)) { if (self->taglist == NULL) { gst_event_parse_tag(event, &self->taglist); } else { @@ -540,6 +570,9 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) /* FIXME handle tag events */ } + if (parent != NULL) + gst_object_unref(GST_OBJECT(parent)); + return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); } @@ -562,10 +595,40 @@ failed: return FALSE; } +static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self) +{ + if (self->fakesink != NULL) + return TRUE; + + g_mutex_lock (self->cb_mutex); + self->fakesink = gst_a2dp_sink_init_element(self, "fakesink", + "fakesink", self->capsfilter); + g_mutex_unlock (self->cb_mutex); + + if (!self->fakesink) + return FALSE; + + return TRUE; +} + +static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self) +{ + g_mutex_lock(self->cb_mutex); + if (self->fakesink != NULL) { + gst_element_set_state(self->fakesink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), self->fakesink); + self->fakesink = NULL; + } + g_mutex_unlock(self->cb_mutex); + + return TRUE; +} + static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass) { self->sink = NULL; + self->fakesink = NULL; self->rtp = NULL; self->device = NULL; self->capsfilter = NULL; @@ -574,12 +637,16 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, self->ghostpad = NULL; self->sink_is_in_bin = FALSE; + self->cb_mutex = g_mutex_new(); + /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); g_object_set(self->capsfilter, "caps", gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), NULL); + gst_a2dp_sink_init_fakesink(self); + gst_a2dp_sink_init_ghost_pad(self); } diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index 26da4c47..d79307b2 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -25,6 +25,9 @@ #include #include "gstavdtpsink.h" +#ifndef __GST_A2DP_SINK_H__ +#define __GST_A2DP_SINK_H__ + G_BEGIN_DECLS #define GST_TYPE_A2DP_SINK \ @@ -47,6 +50,7 @@ struct _GstA2dpSink { GstBaseRTPPayload *rtp; GstAvdtpSink *sink; GstElement *capsfilter; + GstElement *fakesink; gchar *device; gboolean sink_is_in_bin; @@ -59,6 +63,8 @@ struct _GstA2dpSink { /* Store the tags received before the a2dpsender sink is created * when it is created we forward this to it */ GstTagList *taglist; + + GMutex *cb_mutex; }; struct _GstA2dpSinkClass { @@ -68,4 +74,9 @@ struct _GstA2dpSinkClass { GType gst_a2dp_sink_get_type(void); gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin); +GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self); + G_END_DECLS + +#endif + diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 68aa28a9..fdb42d18 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -193,10 +193,10 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, max_payload); g_free(data); - /* FIXME - timestamp it! */ + GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", max_payload); - return gst_basertppayload_push (GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); + return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); } static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, @@ -208,6 +208,7 @@ static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, sbcpay = GST_RTP_SBC_PAY(payload); gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); + sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer); available = gst_adapter_available(sbcpay->adapter); if (available + RTP_SBC_HEADER_TOTAL >= GST_BASE_RTP_PAYLOAD_MTU(sbcpay) || @@ -325,6 +326,7 @@ static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass) { self->adapter = gst_adapter_new(); self->frame_length = 0; + self->timestamp = 0; self->min_frames = DEFAULT_MIN_FRAMES; } diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h index f086a1c7..474d720a 100644 --- a/audio/gstrtpsbcpay.h +++ b/audio/gstrtpsbcpay.h @@ -48,6 +48,7 @@ struct _GstRtpSBCPay { GstBaseRTPPayload base; GstAdapter *adapter; + GstClockTime timestamp; guint frame_length; -- cgit From 6f15a199e037d789188b3b89e77ac78acee7812f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 24 Jan 2008 11:16:15 +0000 Subject: Add some buffer length checks --- audio/headset.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a02f4fef..6972daf9 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -176,6 +176,9 @@ static int supported_features(struct device *device, const char *buf) struct headset *hs = device->headset; int err; + if (strlen(buf) < 9) + return -EINVAL; + hs->hfp_features = strtoul(&buf[8], NULL, 10); err = headset_send(hs, "\r\n+BRSF=%u\r\n", ag_features); if (err < 0) @@ -287,6 +290,9 @@ static int cli_notification(struct device *device, const char *buf) { struct headset *hs = device->headset; + if (strlen(buf) < 9) + return -EINVAL; + hs->cli_active = buf[8] == '1' ? TRUE : FALSE; return headset_send(hs, "\r\nOK\r\n"); -- cgit From 449c9728f515a3b1263f6961c608328b3ad582a9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 24 Jan 2008 14:25:29 +0000 Subject: Fix gtreamer payloader sending fragmented frames. --- audio/gstrtpsbcpay.c | 16 ++++++++++------ audio/gstsbcutil.c | 21 ++++++++++++++++++--- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index fdb42d18..25bf70eb 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -162,6 +162,8 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) GstBuffer* outbuf; guint8 *payload_data; guint8 *data; + guint frame_count; + guint payload_length; struct rtp_payload *payload; if (sbcpay->frame_length == 0) { @@ -176,25 +178,27 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) 0, 0); max_payload = MIN(max_payload, available); + frame_count = max_payload / sbcpay->frame_length; + payload_length = frame_count * sbcpay->frame_length; - outbuf = gst_rtp_buffer_new_allocate(max_payload + + outbuf = gst_rtp_buffer_new_allocate(payload_length + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); gst_rtp_buffer_set_payload_type(outbuf, GST_BASE_RTP_PAYLOAD_PT(sbcpay)); - data = gst_adapter_take(sbcpay->adapter, max_payload); payload_data = gst_rtp_buffer_get_payload(outbuf); - payload = (struct rtp_payload*) payload_data; memset(payload, 0, sizeof(struct rtp_payload)); - payload->frame_count = max_payload / sbcpay->frame_length; + payload->frame_count = frame_count; - memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, max_payload); + data = gst_adapter_take(sbcpay->adapter, payload_length); + memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, + payload_length); g_free(data); GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; - GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", max_payload); + GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", payload_length); return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); } diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 04b7217a..78024f7e 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -100,8 +100,8 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *list) for (i = 0; i < size; i++) { value = gst_value_list_get_value(list, i); aux = g_value_get_string(value); - if (strcmp("stereo", aux) == 0) { - return "stereo"; + if (strcmp("joint", aux) == 0) { + return "joint"; } } return g_value_get_string(gst_value_list_get_value(list, size-1)); @@ -135,6 +135,19 @@ gint gst_sbc_get_mode_int(const gchar *mode) return -1; } +/* FIXME add dual when sbc_t supports it */ +gboolean gst_sbc_get_mode_int_for_sbc_t(const gchar *mode) +{ + if (g_ascii_strcasecmp(mode, "joint") == 0) + return TRUE; + else if (g_ascii_strcasecmp(mode, "stereo") == 0) + return FALSE; + else if (g_ascii_strcasecmp(mode, "mono") == 0) + return FALSE; + else + return -1; +} + const gchar *gst_sbc_get_mode_string(int joint) { switch (joint) { @@ -183,6 +196,8 @@ const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint) return "joint"; else if (channels == 2 && joint == 0) return "stereo"; + else if (channels == 1 && joint == 0) + return "mono"; else return NULL; } @@ -434,7 +449,7 @@ gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps) sbc->blocks = blocks; sbc->subbands = subbands; sbc->bitpool = bitpool; - sbc->joint = gst_sbc_get_mode_int(mode); + sbc->joint = gst_sbc_get_mode_int_for_sbc_t(mode); sbc->allocation = gst_sbc_get_allocation_mode_int(allocation); return TRUE; -- cgit From ba255beb79afb9c00ae5b71821f84f911aa8d1fe Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 28 Jan 2008 10:38:40 +0000 Subject: Whitespace cleanup --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 55addb0b..30716f44 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1259,7 +1259,7 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, error("Too short suspend request"); return FALSE; } - + seid_count = 1 + size - sizeof(struct suspend_req); seid = &req->first_seid; -- cgit From 9fa2613525721908ec43189794fe99828b8a4f51 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jan 2008 11:44:57 +0000 Subject: Add autoconnect config option to IPC and alsa --- audio/ipc.h | 3 +++ audio/pcm_bluetooth.c | 20 +++++++++++++++++++- audio/unix.c | 10 ++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/ipc.h b/audio/ipc.h index 3768dcfb..4da7d2ae 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -117,10 +117,13 @@ typedef struct { #define BT_CAPABILITIES_ACCESS_MODE_WRITE 2 #define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3 +#define BT_FLAG_AUTOCONNECT 1 + struct bt_getcapabilities_req { bt_audio_msg_header_t h; char device[18]; /* Address of the remote Device */ uint8_t transport; /* Requested transport */ + uint8_t flags; /* Requested flags */ } __attribute__ ((packed)); /* BT_GETCAPABILITIES_RSP */ diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index a04f18c0..e8a524c8 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -122,6 +122,7 @@ struct bluetooth_alsa_config { int has_block_length; uint8_t bitpool; /* A2DP only */ int has_bitpool; + int autoconnect; }; struct bluetooth_data { @@ -1268,11 +1269,14 @@ static int bluetooth_parse_config(snd_config_t *conf, struct bluetooth_alsa_config *bt_config) { snd_config_iterator_t i, next; - const char *addr, *pref; + const char *addr, *pref, *autoconnect; const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; memset(bt_config, 0, sizeof(struct bluetooth_alsa_config)); + /* Set defaults */ + bt_config->autoconnect = 1; + snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; @@ -1283,6 +1287,17 @@ static int bluetooth_parse_config(snd_config_t *conf, if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) continue; + if (strcmp(id, "autoconnect") == 0) { + if (snd_config_get_string(n, &autoconnect) < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + + if (strcmp(autoconnect, "no") == 0) + bt_config->autoconnect = 0; + continue; + } + if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) { if (snd_config_get_string(n, &addr) < 0) { SNDERR("Invalid type for %s", id); @@ -1514,6 +1529,9 @@ static int bluetooth_init(struct bluetooth_data *data, snd_pcm_stream_t stream, memset(getcaps_req, 0, BT_AUDIO_IPC_PACKET_SIZE); getcaps_req->h.msg_type = BT_GETCAPABILITIES_REQ; + getcaps_req->flags = 0; + if (alsa_conf->autoconnect) + getcaps_req->flags |= BT_FLAG_AUTOCONNECT; strncpy(getcaps_req->device, alsa_conf->device, 18); if (alsa_conf->has_transport) getcaps_req->transport = alsa_conf->transport; diff --git a/audio/unix.c b/audio/unix.c index d71c420a..f3c6e6aa 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -765,6 +765,8 @@ static void handle_getcapabilities_req(struct unix_client *client, client->interface = g_strdup(AUDIO_SINK_INTERFACE); if (!manager_find_device(&bdaddr, NULL, FALSE)) { + if (!(req->flags & BT_FLAG_AUTOCONNECT)) + goto failed; if (!bacmp(&bdaddr, BDADDR_ANY)) goto failed; if (!manager_create_device(&bdaddr, create_cb, client)) @@ -773,8 +775,12 @@ static void handle_getcapabilities_req(struct unix_client *client, } dev = manager_find_device(&bdaddr, client->interface, TRUE); - if (!dev) - dev = manager_find_device(&bdaddr, client->interface, FALSE); + if (!dev) { + if (req->flags & BT_FLAG_AUTOCONNECT) + dev = manager_find_device(&bdaddr, client->interface, FALSE); + else + goto failed; + } if (!dev) goto 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') 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 d1c580e150dc4982c9e91b70842aab5ba71325eb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jan 2008 12:06:09 +0000 Subject: Clean up unecessary variables from alsa config parsing --- audio/pcm_bluetooth.c | 60 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index e8a524c8..2ecd1680 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -1269,8 +1269,6 @@ static int bluetooth_parse_config(snd_config_t *conf, struct bluetooth_alsa_config *bt_config) { snd_config_iterator_t i, next; - const char *addr, *pref, *autoconnect; - const char *mode, *allocation, *rate, *subbands, *blocks, *bitpool; memset(bt_config, 0, sizeof(struct bluetooth_alsa_config)); @@ -1279,7 +1277,7 @@ static int bluetooth_parse_config(snd_config_t *conf, snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); - const char *id; + const char *id, *value; if (snd_config_get_id(n, &id) < 0) continue; @@ -1288,42 +1286,42 @@ static int bluetooth_parse_config(snd_config_t *conf, continue; if (strcmp(id, "autoconnect") == 0) { - if (snd_config_get_string(n, &autoconnect) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(autoconnect, "no") == 0) + if (strcmp(value, "no") == 0) bt_config->autoconnect = 0; continue; } if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) { - if (snd_config_get_string(n, &addr) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } bt_config->has_device = 1; - strncpy(bt_config->device, addr, 18); + strncpy(bt_config->device, value, 18); continue; } if (strcmp(id, "profile") == 0) { - if (snd_config_get_string(n, &pref) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(pref, "auto") == 0) { + if (strcmp(value, "auto") == 0) { bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY; bt_config->has_transport = 1; - } else if (strcmp(pref, "voice") == 0 || - strcmp(pref, "hfp") == 0) { + } else if (strcmp(value, "voice") == 0 || + strcmp(value, "hfp") == 0) { bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO; bt_config->has_transport = 1; - } else if (strcmp(pref, "hifi") == 0 || - strcmp(pref, "a2dp") == 0) { + } else if (strcmp(value, "hifi") == 0 || + strcmp(value, "a2dp") == 0) { bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP; bt_config->has_transport = 1; } @@ -1331,35 +1329,35 @@ static int bluetooth_parse_config(snd_config_t *conf, } if (strcmp(id, "rate") == 0) { - if (snd_config_get_string(n, &rate) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - bt_config->rate = atoi(rate); + bt_config->rate = atoi(value); bt_config->has_rate = 1; continue; } if (strcmp(id, "mode") == 0) { - if (snd_config_get_string(n, &mode) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(mode, "auto") == 0) { + if (strcmp(value, "auto") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; bt_config->has_channel_mode = 1; - } else if (strcmp(mode, "mono") == 0) { + } else if (strcmp(value, "mono") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; bt_config->has_channel_mode = 1; - } else if (strcmp(mode, "dual") == 0) { + } else if (strcmp(value, "dual") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; bt_config->has_channel_mode = 1; - } else if (strcmp(mode, "stereo") == 0) { + } else if (strcmp(value, "stereo") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; bt_config->has_channel_mode = 1; - } else if (strcmp(mode, "joint") == 0) { + } else if (strcmp(value, "joint") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; bt_config->has_channel_mode = 1; } @@ -1367,18 +1365,18 @@ static int bluetooth_parse_config(snd_config_t *conf, } if (strcmp(id, "allocation") == 0) { - if (snd_config_get_string(n, &allocation) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(allocation, "auto") == 0) { + if (strcmp(value, "auto") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO; bt_config->has_allocation_method = 1; - } else if (strcmp(allocation, "loudness") == 0) { + } else if (strcmp(value, "loudness") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; bt_config->has_allocation_method = 1; - } else if (strcmp(allocation, "snr") == 0) { + } else if (strcmp(value, "snr") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR; bt_config->has_allocation_method = 1; } @@ -1386,34 +1384,34 @@ static int bluetooth_parse_config(snd_config_t *conf, } if (strcmp(id, "subbands") == 0) { - if (snd_config_get_string(n, &subbands) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - bt_config->subbands = atoi(subbands); + bt_config->subbands = atoi(value); bt_config->has_subbands = 1; continue; } if (strcmp(id, "blocks") == 0) { - if (snd_config_get_string(n, &blocks) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - bt_config->block_length = atoi(blocks); + bt_config->block_length = atoi(value); bt_config->has_block_length = 1; continue; } if (strcmp(id, "bitpool") == 0) { - if (snd_config_get_string(n, &bitpool) < 0) { + if (snd_config_get_string(n, &value) < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - bt_config->bitpool = atoi(bitpool); + bt_config->bitpool = atoi(value); bt_config->has_bitpool = 1; continue; } -- cgit From a104e5ff05aa758b5499b316d1923f1c83915e55 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 30 Jan 2008 12:19:56 +0000 Subject: Use snd_config_get_bool for autoconnect --- audio/pcm_bluetooth.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2ecd1680..e3238b9c 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -1286,13 +1286,15 @@ static int bluetooth_parse_config(snd_config_t *conf, continue; if (strcmp(id, "autoconnect") == 0) { - if (snd_config_get_string(n, &value) < 0) { + int b; + + b = snd_config_get_bool(n); + if (b < 0) { SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(value, "no") == 0) - bt_config->autoconnect = 0; + bt_config->autoconnect = b; continue; } -- cgit From 3ad6867c8c7251c3192378a1a0e2ed937ee47d1b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 30 Jan 2008 14:21:43 +0000 Subject: Fixes gstreamer caps and code cleanup. --- audio/gsta2dpsink.c | 40 ++++++++++++++++------- audio/gstavdtpsink.c | 53 ++++++++++++++++++------------ audio/gstavdtpsink.h | 1 - audio/gstrtpsbcpay.c | 13 ++++++-- audio/gstsbcdec.c | 45 +++++++++++++++---------- audio/gstsbcdec.h | 3 ++ audio/gstsbcparse.c | 92 +++------------------------------------------------- audio/gstsbcutil.c | 9 +++-- 8 files changed, 113 insertions(+), 143 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index a2b3286c..b5b1c576 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -80,6 +80,17 @@ static void gst_a2dp_sink_finalize(GObject *obj) G_OBJECT_CLASS (parent_class)->finalize (obj); } +static GstState gst_a2dp_sink_get_state(GstA2dpSink *self) +{ + GstState current, pending; + + gst_element_get_state(GST_ELEMENT(self), ¤t, &pending, 0); + if (pending == GST_STATE_VOID_PENDING) + return current; + + return pending; +} + /* * Helper function to create elements, add to the bin and link it * to another element. @@ -89,6 +100,7 @@ static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, GstElement *link_to) { GstElement *element; + GstState state; GST_LOG_OBJECT(self, "Initializing %s", elementname); @@ -104,17 +116,19 @@ static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, goto cleanup_and_fail; } - if (gst_element_set_state(element, GST_STATE_PLAYING) == + state = gst_a2dp_sink_get_state(self); + if (gst_element_set_state(element, state) == GST_STATE_CHANGE_FAILURE) { GST_ERROR_OBJECT(self, "%s failed to go to playing", elementname); goto remove_element_and_fail; } - if (!gst_element_link(link_to, element)) { - GST_ERROR_OBJECT(self, "couldn't link %s", elementname); - goto remove_element_and_fail; - } + if (link_to != NULL) + if (!gst_element_link(link_to, element)) { + GST_ERROR_OBJECT(self, "couldn't link %s", elementname); + goto remove_element_and_fail; + } return element; @@ -241,7 +255,6 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, case GST_STATE_CHANGE_NULL_TO_READY: self->sink_is_in_bin = FALSE; - self->sink = GST_AVDTP_SINK(gst_element_factory_make( "avdtpsink", "avdtpsink")); if (self->sink == NULL) { @@ -260,8 +273,10 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, break; } - if (ret == GST_STATE_CHANGE_FAILURE) + if (ret == GST_STATE_CHANGE_FAILURE) { + g_mutex_unlock(self->cb_mutex); return ret; + } ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); @@ -295,7 +310,6 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_a2dp_sink_remove_dynamic_elements(self); break; - default: break; } @@ -558,16 +572,16 @@ static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) if (self->newseg_event != NULL) gst_event_unref(self->newseg_event); self->newseg_event = gst_event_ref(event); + } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && parent != GST_OBJECT_CAST(self)) { - if (self->taglist == NULL) { + if (self->taglist == NULL) gst_event_parse_tag(event, &self->taglist); - } else { + else { gst_event_parse_tag(event, &taglist); gst_tag_list_insert(self->taglist, taglist, GST_TAG_MERGE_REPLACE); } - /* FIXME handle tag events */ } if (parent != NULL) @@ -614,11 +628,15 @@ static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self) static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self) { g_mutex_lock(self->cb_mutex); + if (self->fakesink != NULL) { + gst_element_set_locked_state(self->fakesink, TRUE); gst_element_set_state(self->fakesink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(self), self->fakesink); self->fakesink = NULL; } + g_mutex_unlock(self->cb_mutex); return TRUE; diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c index c956ecbd..25fc1ced 100644 --- a/audio/gstavdtpsink.c +++ b/audio/gstavdtpsink.c @@ -68,7 +68,7 @@ struct bluetooth_data { }; #define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) -#define IS_MPEG(n) (strcmp((n), "audio/mpeg") == 0) +#define IS_MPEG_AUDIO(n) (strcmp((n), "audio/mpeg") == 0) enum { PROP_0, @@ -87,8 +87,12 @@ static const GstElementDetails avdtp_sink_details = static GstStaticPadTemplate avdtp_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS("application/x-rtp, " - "media = (string) \"audio\", " - "encoding-name = (string) \"SBC\";" + "media = (string) \"audio\"," + "payload = (int) " + GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, " + "44100, 48000 }, " + "encoding-name = (string) \"SBC\"; " "application/x-rtp, " "media = (string) \"audio\", " "payload = (int) " @@ -231,7 +235,7 @@ static gint gst_avdtp_sink_bluetooth_recvmsg_fd(GstAvdtpSink *sink) return 0; } -static gboolean gst_avdtp_sink_init_pkt_conf(GstAvdtpSink *sink, +static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink, GstCaps *caps, sbc_capabilities_t *pkt) { @@ -242,9 +246,10 @@ static gboolean gst_avdtp_sink_init_pkt_conf(GstAvdtpSink *sink, GstStructure *structure = gst_caps_get_structure(caps, 0); name = gst_structure_get_name(structure); - /* FIXME only sbc supported here, should suport mp3 */ + if (!(IS_SBC(name))) { - GST_ERROR_OBJECT(sink, "Unsupported format %s", name); + GST_ERROR_OBJECT(sink, "Unexpected format %s, " + "was expecting sbc", name); return FALSE; } @@ -814,7 +819,7 @@ static void gst_avdtp_sink_tag(const GstTagList *taglist, if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { GST_WARNING_OBJECT(self, "failed to get crc tag"); - self->mpeg_stream_changed = TRUE; + return; } gst_avdtp_sink_set_crc(self, crc); @@ -824,7 +829,7 @@ static void gst_avdtp_sink_tag(const GstTagList *taglist, if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { GST_WARNING_OBJECT(self, "failed to get channel-mode tag"); - self->mpeg_stream_changed = TRUE; + return; } self->channel_mode = gst_avdtp_sink_get_channel_mode( @@ -883,7 +888,6 @@ static gboolean gst_avdtp_sink_start(GstBaseSink *basesink) self->stream_caps = NULL; self->mp3_using_crc = -1; self->channel_mode = -1; - self->mpeg_stream_changed = FALSE; if (!gst_avdtp_sink_get_capabilities(self)) { GST_ERROR_OBJECT(self, "failed to get capabilities " @@ -957,8 +961,17 @@ static gboolean gst_avdtp_sink_init_mp3_pkt_conf( { const GValue *value = NULL; gint rate, layer; + const gchar* name; GstStructure *structure = gst_caps_get_structure(caps, 0); + name = gst_structure_get_name(structure); + + if (!(IS_MPEG_AUDIO(name))) { + GST_ERROR_OBJECT(self, "Unexpected format %s, " + "was expecting mp3", name); + return FALSE; + } + /* layer */ value = gst_structure_get_value(structure, "layer"); layer = g_value_get_int(value); @@ -1016,9 +1029,6 @@ static gboolean gst_avdtp_sink_init_mp3_pkt_conf( /* vbr - we always say its vbr, we don't have how to know it */ pkt->bitrate = 0x8000; - /* bitrate - we don't set anything, its vbr */ - /* FIXME - is this right? */ - return TRUE; } @@ -1044,7 +1054,7 @@ static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self, structure = gst_caps_get_structure(caps, 0); if (gst_structure_has_name(structure, "audio/x-sbc")) - ret = gst_avdtp_sink_init_pkt_conf(self, caps, + ret = gst_avdtp_sink_init_sbc_pkt_conf(self, caps, &req->sbc_capabilities); else if (gst_structure_has_name(structure, "audio/mpeg")) ret = gst_avdtp_sink_init_mp3_pkt_conf(self, caps, @@ -1188,8 +1198,8 @@ static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass) "Bluetooth remote device address", NULL, G_PARAM_READWRITE)); - GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "a2dpsendersink", 0, - "A2DP sink element"); + GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0, + "A2DP headset sink element"); } static void gst_avdtp_sink_init(GstAvdtpSink *self, @@ -1203,6 +1213,11 @@ static void gst_avdtp_sink_init(GstAvdtpSink *self, self->dev_caps = NULL; self->sink_lock = g_mutex_new(); + + /* FIXME this is for not synchronizing with clock, should be tested + * with devices to see the behaviour + gst_base_sink_set_sync(GST_BASE_SINK(self), FALSE); + */ } static GIOError gst_avdtp_sink_audioservice_send( @@ -1325,9 +1340,7 @@ void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc) /* test if we already received a different crc */ if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { - GST_ERROR_OBJECT(self, "crc changed during stream"); - /* FIXME test this, its not being used anywhere */ - self->mpeg_stream_changed = TRUE; + GST_WARNING_OBJECT(self, "crc changed during stream"); return; } self->mp3_using_crc = new_crc; @@ -1342,8 +1355,8 @@ void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, new_mode = gst_avdtp_sink_get_channel_mode(mode); if (self->channel_mode != -1 && new_mode != self->channel_mode) { - GST_ERROR_OBJECT(self, "channel mode changed during stream"); - self->mpeg_stream_changed = TRUE; + GST_WARNING_OBJECT(self, "channel mode changed during stream"); + return; } self->channel_mode = new_mode; diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h index d0380829..237597da 100644 --- a/audio/gstavdtpsink.h +++ b/audio/gstavdtpsink.h @@ -57,7 +57,6 @@ struct _GstAvdtpSink { GIOChannel *server; /* mp3 stream data (outside caps data)*/ - gboolean mpeg_stream_changed; gint mp3_using_crc; gint channel_mode; diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 25bf70eb..ba2a4741 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -77,7 +77,7 @@ static const GstElementDetails gst_rtp_sbc_pay_details = static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " /* FIXME remove those caps? */ + GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " "mode = (string) { mono, dual, stereo, joint }, " @@ -89,7 +89,12 @@ static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS("application/x-rtp") /* FIXME put things here */ + GST_STATIC_CAPS( + "application/x-rtp, " + "media = (string) \"audio\"," + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, 44100, 48000 }," + "encoding-name = (string) \"SBC\"") ); static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, @@ -148,7 +153,7 @@ static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload, sbcpay->frame_length = frame_len; - gst_basertppayload_set_options (payload, "audio", FALSE, "SBC", rate); + gst_basertppayload_set_options (payload, "audio", TRUE, "SBC", rate); GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len); @@ -209,6 +214,8 @@ static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, GstRtpSBCPay *sbcpay; guint available; + /* FIXME check for negotiation */ + sbcpay = GST_RTP_SBC_PAY(payload); gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 8c27daba..a686a406 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -80,26 +80,12 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) while (offset < size) { GstBuffer *output; GstPadTemplate *template; - GstCaps *caps, *temp; + GstCaps *caps; int consumed; - caps = gst_caps_new_simple("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sbc.rate, - "channels", G_TYPE_INT, dec->sbc.channels, - NULL); - - template = gst_static_pad_template_get(&sbc_dec_src_factory); - - temp = gst_caps_intersect(caps, - gst_pad_template_get_caps(template)); - - gst_caps_unref(caps); - res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, GST_BUFFER_OFFSET_NONE, - codesize, temp, &output); - - gst_caps_unref(temp); + codesize, NULL, &output); if (res != GST_FLOW_OK) goto done; @@ -110,7 +96,25 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) if (consumed <= 0) break; - GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); + /* we will reuse the same caps object */ + if (dec->outcaps == NULL) { + caps = gst_caps_new_simple("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sbc.rate, + "channels", G_TYPE_INT, dec->sbc.channels, + NULL); + + template = gst_static_pad_template_get(&sbc_dec_src_factory); + + dec->outcaps = gst_caps_intersect(caps, + gst_pad_template_get_caps(template)); + + gst_caps_unref(caps); + } + + gst_buffer_set_caps(output, dec->outcaps); + + /* FIXME get a real timestamp */ + GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE; res = gst_pad_push(dec->srcpad, output); if (res != GST_FLOW_OK) @@ -143,6 +147,7 @@ static GstStateChangeReturn sbc_dec_change_state(GstElement *element, dec->buffer = NULL; } sbc_init(&dec->sbc, 0); + dec->outcaps = NULL; break; case GST_STATE_CHANGE_PAUSED_TO_READY: @@ -152,6 +157,10 @@ static GstStateChangeReturn sbc_dec_change_state(GstElement *element, dec->buffer = NULL; } sbc_finish(&dec->sbc); + if (dec->outcaps) { + gst_caps_unref(dec->outcaps); + dec->outcaps = NULL; + } break; default: @@ -197,6 +206,8 @@ static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) self->srcpad = gst_pad_new_from_static_template( &sbc_dec_src_factory, "src"); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + + self->outcaps = NULL; } gboolean gst_sbc_dec_plugin_init (GstPlugin * plugin) diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index 0bb0b57e..a88f8da5 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -49,6 +49,9 @@ struct _GstSbcDec { GstBuffer *buffer; + /* caps for outgoing buffers */ + GstCaps *outcaps; + sbc_t sbc; }; diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 49d0bb6e..1f699620 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -43,7 +43,8 @@ static const GstElementDetails sbc_parse_details = static GstStaticPadTemplate sbc_parse_sink_factory = GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc")); + GST_STATIC_CAPS("audio/x-sbc," + "parsed = (boolean) false")); static GstStaticPadTemplate sbc_parse_src_factory = GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -54,86 +55,8 @@ static GstStaticPadTemplate sbc_parse_src_factory = "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }," - "bitpool = (int) [ 2, 64 ]")); - -static gboolean sbc_parse_sink_setcaps(GstPad * pad, GstCaps * caps) -{ - GstSbcParse *parse; - GstStructure *structure; - gint rate, channels; - - parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); - - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - - if (!(parse->rate == 0 || rate == parse->rate)) - return FALSE; - - if (!(parse->channels == 0 || channels == parse->channels)) - return FALSE; - - parse->rate = rate; - parse->channels = channels; - - return gst_sbc_util_fill_sbc_params(&parse->sbc, caps); -} - -static GstCaps* sbc_parse_src_getcaps(GstPad *pad) -{ - GstCaps *caps; - const GstCaps *allowed_caps; - GstStructure *structure; - GValue *value; - GstSbcParse *parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); - - allowed_caps = gst_pad_get_allowed_caps(pad); - if (allowed_caps == NULL) - allowed_caps = gst_pad_get_pad_template_caps(pad); - caps = gst_caps_copy(allowed_caps); - - value = g_new0(GValue, 1); - - structure = gst_caps_get_structure(caps, 0); - - if (parse->rate != 0) - gst_sbc_util_set_structure_int_param(structure, "rate", - parse->rate, value); - if (parse->channels != 0) - gst_sbc_util_set_structure_int_param(structure, "channels", - parse->channels, value); - - g_free(value); - - return caps; -} - -static gboolean sbc_parse_src_acceptcaps(GstPad *pad, GstCaps *caps) -{ - GstStructure *structure; - GstSbcParse *parse; - gint rate, channels; - - parse = GST_SBC_PARSE(GST_PAD_PARENT(pad)); - - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - - if ((parse->rate == 0 || parse->rate == rate) - && (parse->channels == 0 || parse->channels == channels)) - return TRUE; - - return FALSE; -} + "bitpool = (int) [ 2, 64 ]," + "parsed = (boolean) true")); static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) { @@ -261,17 +184,10 @@ static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) &sbc_parse_sink_factory, "sink"); gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(sbc_parse_chain)); - gst_pad_set_setcaps_function (self->sinkpad, - GST_DEBUG_FUNCPTR (sbc_parse_sink_setcaps)); gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); self->srcpad = gst_pad_new_from_static_template( &sbc_parse_src_factory, "src"); - gst_pad_set_getcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (sbc_parse_src_getcaps)); - gst_pad_set_acceptcaps_function (self->srcpad, - GST_DEBUG_FUNCPTR (sbc_parse_src_acceptcaps)); - /* FIXME get encoding parameters on set caps */ gst_element_add_pad(GST_ELEMENT(self), self->srcpad); } diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 78024f7e..e5d21af0 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -364,9 +364,12 @@ GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message) goto error; } else { value = gst_structure_get_value(structure, "mode"); - if (GST_VALUE_HOLDS_LIST(value)) - mode = gst_sbc_get_mode_from_list(value); - else + if (GST_VALUE_HOLDS_LIST(value)) { + if (channels == 1) + mode = "mono"; + else + mode = gst_sbc_get_mode_from_list(value); + } else mode = g_value_get_string(value); } -- cgit From 6c4268df1dff13f3b1a7b778eb2e993648bff519 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 30 Jan 2008 17:30:27 +0000 Subject: Enable gstreamer plugin to use autoconnect flag. --- audio/gsta2dpsink.c | 28 +++++++++++++++++++++++++++- audio/gsta2dpsink.h | 1 + audio/gstavdtpsink.c | 20 ++++++++++++++++++++ audio/gstavdtpsink.h | 1 + audio/gstsbcparse.c | 41 ++++++++++++++++++++++++++++++++--------- audio/gstsbcparse.h | 3 +++ 6 files changed, 84 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index b5b1c576..6595d0b5 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -37,9 +37,12 @@ GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug); #define A2DP_SBC_RTP_PAYLOAD_TYPE 1 #define TEMPLATE_MAX_BITPOOL_STR "64" +#define DEFAULT_AUTOCONNECT TRUE + enum { PROP_0, - PROP_DEVICE + PROP_DEVICE, + PROP_AUTOCONNECT }; GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN); @@ -170,6 +173,14 @@ static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, self->device = g_value_dup_string(value); break; + case PROP_AUTOCONNECT: + self->autoconnect = g_value_get_boolean(value); + + if (self->sink != NULL) + g_object_set(G_OBJECT(self->sink), "auto-connect", + self->autoconnect, NULL); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -190,7 +201,13 @@ static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, g_value_take_string(value, device); } break; + case PROP_AUTOCONNECT: + if (self->sink != NULL) + g_object_get(G_OBJECT(self->sink), "auto-connect", + &self->autoconnect, NULL); + g_value_set_boolean(value, self->autoconnect); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -266,6 +283,9 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, gst_avdtp_sink_set_device(self->sink, self->device); + g_object_set(G_OBJECT(self->sink), "auto-connect", + self->autoconnect, NULL); + ret = gst_element_set_state(GST_ELEMENT(self->sink), GST_STATE_READY); break; @@ -340,6 +360,11 @@ static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) "Bluetooth remote device address", NULL, G_PARAM_READWRITE)); + g_object_class_install_property(object_class, PROP_AUTOCONNECT, + g_param_spec_boolean("auto-connect", "Auto-connect", + "Automatically attempt to connect to device", + DEFAULT_AUTOCONNECT, G_PARAM_READWRITE)); + GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0, "A2DP sink element"); } @@ -649,6 +674,7 @@ static void gst_a2dp_sink_init(GstA2dpSink *self, self->fakesink = NULL; self->rtp = NULL; self->device = NULL; + self->autoconnect = DEFAULT_AUTOCONNECT; self->capsfilter = NULL; self->newseg_event = NULL; self->taglist = NULL; diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index d79307b2..cf11d43e 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -53,6 +53,7 @@ struct _GstA2dpSink { GstElement *fakesink; gchar *device; + gboolean autoconnect; gboolean sink_is_in_bin; GstGhostPad *ghostpad; diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c index 25fc1ced..d5737258 100644 --- a/audio/gstavdtpsink.c +++ b/audio/gstavdtpsink.c @@ -51,6 +51,8 @@ GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug); #define CRC_PROTECTED 1 #define CRC_UNPROTECTED 0 +#define DEFAULT_AUTOCONNECT TRUE + #define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \ g_mutex_lock (s->sink_lock); \ } G_STMT_END @@ -73,6 +75,7 @@ struct bluetooth_data { enum { PROP_0, PROP_DEVICE, + PROP_AUTOCONNECT }; GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink, @@ -193,6 +196,9 @@ static void gst_avdtp_sink_set_property(GObject *object, guint prop_id, sink->device = g_value_dup_string(value); break; + case PROP_AUTOCONNECT: + sink->autoconnect = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -209,6 +215,9 @@ static void gst_avdtp_sink_get_property(GObject *object, guint prop_id, g_value_set_string(value, sink->device); break; + case PROP_AUTOCONNECT: + g_value_set_boolean(value, sink->autoconnect); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -764,6 +773,8 @@ static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self) if (self->device == NULL) return FALSE; strncpy(req->device, self->device, 18); + if (self->autoconnect) + req->flags |= BT_FLAG_AUTOCONNECT; io_error = gst_avdtp_sink_audioservice_send(self, &req->h); if (io_error != G_IO_ERROR_NONE) { @@ -1198,6 +1209,13 @@ static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass) "Bluetooth remote device address", NULL, G_PARAM_READWRITE)); + g_object_class_install_property(object_class, PROP_AUTOCONNECT, + g_param_spec_boolean("auto-connect", + "Auto-connect", + "Automatically attempt to connect " + "to device", DEFAULT_AUTOCONNECT, + G_PARAM_READWRITE)); + GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0, "A2DP headset sink element"); } @@ -1212,6 +1230,8 @@ static void gst_avdtp_sink_init(GstAvdtpSink *self, self->dev_caps = NULL; + self->autoconnect = DEFAULT_AUTOCONNECT; + self->sink_lock = g_mutex_new(); /* FIXME this is for not synchronizing with clock, should be tested diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h index 237597da..333f1a4b 100644 --- a/audio/gstavdtpsink.h +++ b/audio/gstavdtpsink.h @@ -54,6 +54,7 @@ struct _GstAvdtpSink { GIOChannel *stream; struct bluetooth_data *data; + gboolean autoconnect; GIOChannel *server; /* mp3 stream data (outside caps data)*/ diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 1f699620..80bf23a2 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -68,6 +68,7 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) timestamp = GST_BUFFER_TIMESTAMP(buffer); + /* FIXME use a gstadpter */ if (parse->buffer) { GstBuffer *temp; temp = buffer; @@ -84,18 +85,29 @@ static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) while (offset < size) { GstBuffer *output; - GstCaps *temp; int consumed; - consumed = sbc_parse(&parse->sbc, data + offset, size - offset); + consumed = sbc_parse(&parse->new_sbc, data + offset, + size - offset); if (consumed <= 0) break; - temp = GST_PAD_CAPS(parse->srcpad); + if (parse->first_parsing || (memcmp(&parse->sbc, + &parse->new_sbc, sizeof(sbc_t)) != 0)) { + + memcpy(&parse->sbc, &parse->new_sbc, sizeof(sbc_t)); + if (parse->outcaps != NULL) + gst_caps_unref(parse->outcaps); + + parse->outcaps = gst_sbc_parse_caps_from_sbc( + &parse->sbc); + + parse->first_parsing = FALSE; + } res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad, GST_BUFFER_OFFSET_NONE, - consumed, temp, &output); + consumed, parse->outcaps, &output); if (res != GST_FLOW_OK) goto done; @@ -128,10 +140,11 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: GST_DEBUG("Setup subband codec"); - if (parse->buffer) { - gst_buffer_unref(parse->buffer); - parse->buffer = NULL; - } + + parse->channels = -1; + parse->rate = -1; + parse->first_parsing = TRUE; + sbc_init(&parse->sbc, 0); break; @@ -142,8 +155,12 @@ static GstStateChangeReturn sbc_parse_change_state(GstElement *element, gst_buffer_unref(parse->buffer); parse->buffer = NULL; } - sbc_finish(&parse->sbc); + if (parse->outcaps != NULL) { + gst_caps_unref(parse->outcaps); + parse->outcaps = NULL; + } + sbc_finish(&parse->sbc); break; default: @@ -189,6 +206,12 @@ static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) self->srcpad = gst_pad_new_from_static_template( &sbc_parse_src_factory, "src"); gst_element_add_pad(GST_ELEMENT(self), self->srcpad); + + self->outcaps = NULL; + self->buffer = NULL; + self->channels = -1; + self->rate = -1; + self->first_parsing = TRUE; } gboolean gst_sbc_parse_plugin_init (GstPlugin * plugin) diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h index eb9ca441..a71aea72 100644 --- a/audio/gstsbcparse.h +++ b/audio/gstsbcparse.h @@ -50,6 +50,9 @@ struct _GstSbcParse { GstBuffer *buffer; sbc_t sbc; + sbc_t new_sbc; + GstCaps *outcaps; + gboolean first_parsing; gint channels; gint rate; -- cgit From ad31a843448470c91973a8b4b2a6067a3b3235fc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 Jan 2008 12:38:14 +0000 Subject: Check for client disconnect when discovery completes --- audio/unix.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index f3c6e6aa..b45ab9a5 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -344,6 +344,11 @@ static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, struct mpeg_codec_cap *mpeg_cap = NULL; GSList *l; + if (!g_slist_find(clients, client)) { + debug("Client disconnected during discovery"); + return; + } + if (err) goto failed; -- cgit From 7e64674f8b5c5be6b3cccb17ea05766763d50f94 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 Jan 2008 13:44:03 +0000 Subject: Don't confuse Sink interface with local sink support --- audio/a2dp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index fa5ff2bf..449b72c7 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1035,7 +1035,7 @@ int a2dp_init(DBusConnection *conn, GKeyFile *config) { int sbc_srcs = 1, sbc_sinks = 0; int mpeg12_srcs = 0, mpeg12_sinks = 0; - gboolean src = TRUE, sink = TRUE; + gboolean source = TRUE, sink = TRUE; char *str; GError *err = NULL; int i; @@ -1051,9 +1051,9 @@ int a2dp_init(DBusConnection *conn, GKeyFile *config) err = NULL; } else { if (strstr(str, "Sink")) - sink = FALSE; + source = FALSE; if (strstr(str, "Source")) - src = FALSE; + sink = FALSE; g_free(str); } @@ -1102,7 +1102,7 @@ proceed: avdtp_init(); - if (src) { + if (source) { for (i = 0; i < sbc_srcs; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC); -- cgit From 08fa71cc327de929ebe0c36b3d2eb0fa5190e441 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 31 Jan 2008 14:04:56 +0000 Subject: Remove mentions of Target interface --- audio/audio-api.txt | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 008a194d..63c0a20d 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -300,13 +300,3 @@ AVRCP controller. Object path(s) /org/bluez/audio/device* - -org.bluez.audio.Target interface -================================ - -[not yet implemented] - -This interface is available for remote devices which implement support for a -AVRCP target. - -Object path(s) /org/bluez/audio/device* -- cgit From ae689c59cc62acc9f160afe6beb6ccc07b6c6f55 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 1 Feb 2008 19:28:37 +0000 Subject: Add bitpool property and others fixes for gstreamer plugin. --- audio/gsta2dpsink.c | 13 ++-- audio/gstsbcenc.c | 177 +++++++++++++++++++++++++++++++++++----------------- audio/gstsbcenc.h | 2 + audio/gstsbcutil.c | 64 +++++++++++++++---- audio/gstsbcutil.h | 3 +- 5 files changed, 181 insertions(+), 78 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 6595d0b5..4ff30b84 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -109,12 +109,12 @@ static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, element = gst_element_factory_make(elementname, name); if (element == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create %s", elementname); + GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname); return NULL; } if (!gst_bin_add(GST_BIN(self), element)) { - GST_ERROR_OBJECT(self, "failed to add %s to the bin", + GST_DEBUG_OBJECT(self, "failed to add %s to the bin", elementname); goto cleanup_and_fail; } @@ -122,14 +122,15 @@ static GstElement* gst_a2dp_sink_init_element(GstA2dpSink *self, state = gst_a2dp_sink_get_state(self); if (gst_element_set_state(element, state) == GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "%s failed to go to playing", + GST_DEBUG_OBJECT(self, "%s failed to go to playing", elementname); goto remove_element_and_fail; } if (link_to != NULL) if (!gst_element_link(link_to, element)) { - GST_ERROR_OBJECT(self, "couldn't link %s", elementname); + GST_DEBUG_OBJECT(self, "couldn't link %s", + elementname); goto remove_element_and_fail; } @@ -293,10 +294,8 @@ static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, break; } - if (ret == GST_STATE_CHANGE_FAILURE) { - g_mutex_unlock(self->cb_mutex); + if (ret == GST_STATE_CHANGE_FAILURE) return ret; - } ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition); diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 08ddc14f..88737475 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -34,11 +34,16 @@ #define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_AUTO #define SBC_ENC_DEFAULT_BLOCKS 0 #define SBC_ENC_DEFAULT_SUB_BANDS 0 -#define SBC_ENC_DEFAULT_BITPOOL 0 #define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_AUTO #define SBC_ENC_DEFAULT_RATE 0 #define SBC_ENC_DEFAULT_CHANNELS 0 +#define SBC_ENC_BITPOOL_AUTO 1 +#define SBC_ENC_BITPOOL_MIN 2 +#define SBC_ENC_BITPOOL_MIN_STR "2" +#define SBC_ENC_BITPOOL_MAX 64 +#define SBC_ENC_BITPOOL_MAX_STR "64" + GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); #define GST_CAT_DEFAULT sbc_enc_debug @@ -81,12 +86,53 @@ static GType gst_sbc_allocation_get_type(void) return sbc_allocation_type; } +#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type()) + +static GType gst_sbc_blocks_get_type(void) +{ + static GType sbc_blocks_type = 0; + static GEnumValue sbc_blocks[] = { + { 0, "Auto", "auto" }, + { 4, "4", "4" }, + { 8, "8", "8" }, + { 12, "12", "12" }, + { 16, "16", "16" }, + { -1, NULL, NULL} + }; + + if (!sbc_blocks_type) + sbc_blocks_type = g_enum_register_static( + "GstSbcBlocks", sbc_blocks); + + return sbc_blocks_type; +} + +#define GST_TYPE_SBC_SUBBANDS (gst_sbc_subbands_get_type()) + +static GType gst_sbc_subbands_get_type(void) +{ + static GType sbc_subbands_type = 0; + static GEnumValue sbc_subbands[] = { + { 0, "Auto", "auto" }, + { 4, "4 subbands", "4" }, + { 8, "8 subbands", "8" }, + { -1, NULL, NULL} + }; + + if (!sbc_subbands_type) + sbc_subbands_type = g_enum_register_static( + "GstSbcSubbands", sbc_subbands); + + return sbc_subbands_type; +} + enum { PROP_0, PROP_MODE, PROP_ALLOCATION, PROP_BLOCKS, - PROP_SUBBANDS + PROP_SUBBANDS, + PROP_BITPOOL }; GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); @@ -116,7 +162,8 @@ static GstStaticPadTemplate sbc_enc_src_factory = "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " "allocation = (string) { snr, loudness }," - "bitpool = (int) [ 2, 64 ]")); + "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR + ", " SBC_ENC_BITPOOL_MAX_STR " ]")); gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); @@ -150,6 +197,10 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) gst_sbc_util_set_structure_int_param(structure, "blocks", enc->blocks, value); + if (enc->bitpool != SBC_ENC_BITPOOL_AUTO) + gst_sbc_util_set_structure_int_param(structure, "bitpool", + enc->bitpool, value); + if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); enum_value = g_enum_get_value(enum_class, enc->mode); @@ -201,7 +252,7 @@ static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) result = gst_sbc_util_caps_fixate(caps, &error_message); if (!result) { - GST_ERROR_OBJECT (enc, "Invalid input caps caused parsing " + GST_WARNING_OBJECT (enc, "Invalid input caps caused parsing " "error: %s", error_message); g_free(error_message); return NULL; @@ -212,18 +263,13 @@ static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) static GstCaps* sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc) { - GstCaps *peer_caps; - GstCaps *src_caps; GstCaps *caps; gboolean res = TRUE; GstCaps *result_caps = NULL; - peer_caps = gst_pad_peer_get_caps(enc->srcpad); - if (!peer_caps) - return NULL; - - src_caps = sbc_enc_generate_srcpad_caps(enc); - caps = gst_caps_intersect(src_caps, peer_caps); + caps = gst_pad_get_allowed_caps(enc->srcpad); + if (caps == NULL) + caps = sbc_enc_src_getcaps(enc->srcpad); if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) { res = FALSE; @@ -233,9 +279,6 @@ static GstCaps* sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc) result_caps = sbc_enc_src_caps_fixate(enc, caps); done: - - gst_caps_unref(src_caps); - gst_caps_unref(peer_caps); gst_caps_unref(caps); if (!res) @@ -256,45 +299,71 @@ static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) structure = gst_caps_get_structure(caps, 0); if (!gst_structure_get_int(structure, "rate", &rate)) - goto error; + return FALSE; if (!gst_structure_get_int(structure, "channels", &channels)) - goto error; + return FALSE; enc->rate = rate; enc->channels = channels; src_caps = sbc_enc_get_fixed_srcpad_caps(enc); if (!src_caps) - goto error; + return FALSE; res = gst_pad_set_caps(enc->srcpad, src_caps); gst_caps_unref(src_caps); return res; - -error: - GST_ERROR_OBJECT (enc, "invalid input caps"); - return FALSE; } gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) { + gint mode; + + if (!gst_caps_is_fixed(caps)) { + GST_DEBUG_OBJECT(enc, "didn't receive fixed caps, " + "returning false"); + return FALSE; + } if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps)) return FALSE; - enc->rate = enc->sbc.rate; - enc->channels = enc->sbc.channels; - enc->blocks = enc->sbc.blocks; - enc->subbands = enc->sbc.subbands; - enc->mode = enc->sbc.joint; - enc->allocation = enc->sbc.allocation; + if (enc->rate != 0 && enc->sbc.rate != enc->rate) + goto fail; + + if (enc->channels != 0 && enc->sbc.channels != enc->channels) + goto fail; + + if (enc->blocks != 0 && enc->sbc.blocks != enc->blocks) + goto fail; + + if (enc->subbands != 0 && enc->sbc.subbands != enc->subbands) + goto fail; + + mode = gst_sbc_get_mode_int_from_sbc_t(&enc->sbc); + if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO && mode != enc->mode) + goto fail; + + if (enc->allocation != BT_A2DP_ALLOCATION_AUTO && + enc->sbc.allocation != enc->allocation) + goto fail; + + if (enc->bitpool != SBC_ENC_BITPOOL_AUTO && + enc->sbc.bitpool != enc->bitpool) + goto fail; + enc->codesize = sbc_get_codesize(&enc->sbc); enc->frame_length = sbc_get_frame_length(&enc->sbc); enc->frame_duration = sbc_get_frame_duration(&enc->sbc); + GST_DEBUG("codesize: %d, frame_length: %d, frame_duration: %d", enc->codesize, enc->frame_length, enc->frame_duration); return TRUE; + +fail: + memset(&enc->sbc, 0, sizeof(sbc_t)); + return FALSE; } static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) @@ -393,23 +462,6 @@ static void gst_sbc_enc_base_init(gpointer g_class) gst_element_class_set_details(element_class, &sbc_enc_details); } -static gboolean sbc_enc_set_blocks(GstSbcEnc *enc, gint value) -{ - if (value != 4 && value != 8 && value != 12 && - value != 16 && value != 0) - return FALSE; - enc->blocks = value; - return TRUE; -} - -static gboolean sbc_enc_set_subbands(GstSbcEnc *enc, gint value) -{ - if (value != 4 && value != 8 && value != 0) - return FALSE; - enc->subbands = value; - return TRUE; -} - static void gst_sbc_enc_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { @@ -426,14 +478,13 @@ static void gst_sbc_enc_set_property(GObject *object, guint prop_id, enc->allocation = g_value_get_enum(value); break; case PROP_BLOCKS: - if (!sbc_enc_set_blocks(enc, g_value_get_int(value))) - GST_WARNING_OBJECT(enc, "invalid value %d for " - "blocks property", g_value_get_int(value)); + enc->blocks = g_value_get_enum(value); break; case PROP_SUBBANDS: - if (!sbc_enc_set_subbands(enc, g_value_get_int(value))) - GST_WARNING_OBJECT(enc, "invalid value %d for " - "subbands property", g_value_get_int(value)); + enc->subbands = g_value_get_enum(value); + break; + case PROP_BITPOOL: + enc->bitpool = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -454,10 +505,13 @@ static void gst_sbc_enc_get_property(GObject *object, guint prop_id, g_value_set_enum(value, enc->allocation); break; case PROP_BLOCKS: - g_value_set_int(value, enc->blocks); + g_value_set_enum(value, enc->blocks); break; case PROP_SUBBANDS: - g_value_set_int(value, enc->subbands); + g_value_set_enum(value, enc->subbands); + break; + case PROP_BITPOOL: + g_value_set_int(value, enc->bitpool); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -485,19 +539,25 @@ static void gst_sbc_enc_class_init(GstSbcEncClass *klass) g_object_class_install_property(object_class, PROP_ALLOCATION, g_param_spec_enum("allocation", "Allocation", - "Allocation mode", GST_TYPE_SBC_ALLOCATION, + "Allocation method", GST_TYPE_SBC_ALLOCATION, SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_BLOCKS, - g_param_spec_int("blocks", "Blocks", - "Blocks", 0, G_MAXINT, + g_param_spec_enum("blocks", "Blocks", + "Blocks", GST_TYPE_SBC_BLOCKS, SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_SUBBANDS, - g_param_spec_int("subbands", "Sub Bands", - "Sub Bands", 0, G_MAXINT, + g_param_spec_enum("subbands", "Sub bands", + "Number of sub bands", GST_TYPE_SBC_SUBBANDS, SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE)); + g_object_class_install_property(object_class, PROP_BITPOOL, + g_param_spec_int("bitpool", "Bitpool", + "Bitpool (use 1 for automatic selection)", + SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX, + SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE)); + GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, "SBC encoding element"); } @@ -527,6 +587,7 @@ static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) self->allocation = SBC_ENC_DEFAULT_ALLOCATION; self->rate = SBC_ENC_DEFAULT_RATE; self->channels = SBC_ENC_DEFAULT_CHANNELS; + self->bitpool = SBC_ENC_BITPOOL_AUTO; self->frame_length = 0; self->frame_duration = 0; diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index c7b21638..41417507 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -55,6 +55,8 @@ struct _GstSbcEnc { gint blocks; gint allocation; gint subbands; + gint bitpool; + gint codesize; gint frame_length; gint frame_duration; diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index e5d21af0..7c47dbca 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -90,21 +90,39 @@ const gchar *gst_sbc_get_allocation_from_list(const GValue *value) /* * Selects one mode from the ones on the list */ -const gchar *gst_sbc_get_mode_from_list(const GValue *list) +const gchar *gst_sbc_get_mode_from_list(const GValue *list, gint channels) { int i; const GValue *value; const gchar *aux; + gboolean joint, stereo, dual, mono; + joint = stereo = dual = mono = FALSE; guint size = gst_value_list_get_size(list); + for (i = 0; i < size; i++) { value = gst_value_list_get_value(list, i); aux = g_value_get_string(value); - if (strcmp("joint", aux) == 0) { + if (strcmp("joint", aux) == 0) + joint = TRUE; + else if (strcmp("stereo", aux) == 0) + stereo = TRUE; + else if (strcmp("dual", aux) == 0) + dual = TRUE; + else if (strcmp("mono", aux) == 0) + mono = TRUE; + } + + if (channels == 1 && mono) + return "mono"; + else if (channels == 2) { + if (joint) return "joint"; - } + else if (stereo) + return "stereo"; } - return g_value_get_string(gst_value_list_get_value(list, size-1)); + + return NULL; } gint gst_sbc_get_allocation_mode_int(const gchar *allocation) @@ -148,7 +166,20 @@ gboolean gst_sbc_get_mode_int_for_sbc_t(const gchar *mode) return -1; } -const gchar *gst_sbc_get_mode_string(int joint) +gint gst_sbc_get_mode_int_from_sbc_t(const sbc_t *sbc) +{ + /* TODO define constants */ + if (sbc->channels == 2 && sbc->joint == 1) + return 4; + else if (sbc->channels == 2 && sbc->joint == 0) + return 3; + else if (sbc->channels == 1) + return 1; + else + return -1; +} + +const gchar *gst_sbc_get_mode_string(gint joint) { switch (joint) { case BT_A2DP_CHANNEL_MODE_MONO: @@ -166,7 +197,7 @@ const gchar *gst_sbc_get_mode_string(int joint) } } -const gchar *gst_sbc_get_allocation_string(int alloc) +const gchar *gst_sbc_get_allocation_string(gint alloc) { switch (alloc) { case BT_A2DP_ALLOCATION_LOUDNESS: @@ -190,7 +221,7 @@ const gchar *gst_sbc_get_allocation_string(int alloc) #define SBC_AM_LOUDNESS 0x00 #define SBC_AM_SNR 0x01 -const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint) +const gchar *gst_sbc_get_mode_string_from_sbc_t(gint channels, gint joint) { if (channels == 2 && joint == 1) return "joint"; @@ -202,7 +233,7 @@ const gchar *gst_sbc_get_mode_string_from_sbc_t(int channels, int joint) return NULL; } -const gchar *gst_sbc_get_allocation_string_from_sbc_t(int alloc) +const gchar *gst_sbc_get_allocation_string_from_sbc_t(gint alloc) { switch (alloc) { case SBC_AM_LOUDNESS: @@ -365,14 +396,23 @@ GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message) } else { value = gst_structure_get_value(structure, "mode"); if (GST_VALUE_HOLDS_LIST(value)) { - if (channels == 1) - mode = "mono"; - else - mode = gst_sbc_get_mode_from_list(value); + mode = gst_sbc_get_mode_from_list(value, channels); } else mode = g_value_get_string(value); } + /* perform validation + * if channels is 1, we must have channel mode = mono + * if channels is 2, we can't have channel mode = mono, dual */ + if ( (channels == 1 && (strcmp(mode, "mono") != 0) ) || + ( channels == 2 && ( strcmp(mode, "mono") == 0 || + strcmp(mode, "dual") == 0 ) )) { + *error_message = g_strdup_printf("Invalid combination of " + "channels (%d) and channel mode (%s)", + channels, mode); + error = TRUE; + } + error: if (error) return NULL; diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 4bdb5ac5..9e81af1a 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -42,8 +42,9 @@ const gchar *gst_sbc_get_allocation_from_list(const GValue *value); gint gst_sbc_get_allocation_mode_int(const gchar *allocation); const gchar *gst_sbc_get_allocation_string(int alloc); -const gchar *gst_sbc_get_mode_from_list(const GValue *value); +const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels); gint gst_sbc_get_mode_int(const gchar *mode); +gint gst_sbc_get_mode_int_from_sbc_t(const sbc_t *sbc); const gchar *gst_sbc_get_mode_string(int joint); GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); -- 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/a2dp.c | 2 +- audio/a2dp.h | 2 +- audio/avdtp.c | 2 +- audio/avdtp.h | 2 +- audio/control.c | 2 +- audio/control.h | 2 +- audio/ctl_bluetooth.c | 2 +- audio/device.c | 2 +- audio/device.h | 2 +- audio/gateway.c | 2 +- audio/gateway.h | 2 +- audio/gsta2dpsink.c | 2 +- audio/gsta2dpsink.h | 2 +- audio/gstavdtpsink.c | 2 +- audio/gstavdtpsink.h | 2 +- audio/gstbluetooth.c | 2 +- audio/gstrtpsbcpay.c | 2 +- audio/gstrtpsbcpay.h | 2 +- audio/gstsbcdec.c | 2 +- audio/gstsbcdec.h | 2 +- audio/gstsbcenc.c | 2 +- audio/gstsbcenc.h | 2 +- audio/gstsbcparse.c | 2 +- audio/gstsbcparse.h | 2 +- audio/gstsbcutil.c | 2 +- audio/gstsbcutil.h | 2 +- audio/headset.c | 2 +- audio/headset.h | 2 +- audio/ipc.c | 2 +- audio/ipc.h | 2 +- audio/main.c | 2 +- audio/manager.c | 2 +- audio/manager.h | 2 +- audio/module-bluetooth-sink.c | 2 +- audio/pcm_bluetooth.c | 2 +- audio/rtp.h | 2 +- audio/sink.c | 2 +- audio/sink.h | 2 +- audio/unix.c | 2 +- audio/unix.h | 2 +- 40 files changed, 40 insertions(+), 40 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 449b72c7..fd3cb632 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.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 diff --git a/audio/a2dp.h b/audio/a2dp.h index a83b523a..1e4ab408 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -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 diff --git a/audio/avdtp.c b/audio/avdtp.c index 30716f44..09e2b41a 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.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 diff --git a/audio/avdtp.h b/audio/avdtp.h index 01ca50c6..4ae17508 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -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 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 diff --git a/audio/control.h b/audio/control.h index 42cd4826..7247949d 100644 --- a/audio/control.h +++ b/audio/control.h @@ -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 diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c index f7239f67..a87c3c19 100644 --- a/audio/ctl_bluetooth.c +++ b/audio/ctl_bluetooth.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/device.c b/audio/device.c index a5b9b0a9..2f5bf417 100644 --- a/audio/device.c +++ b/audio/device.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 diff --git a/audio/device.h b/audio/device.h index 04ce36de..c5907075 100644 --- a/audio/device.h +++ b/audio/device.h @@ -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 diff --git a/audio/gateway.c b/audio/gateway.c index 33cffa88..a299d28a 100644 --- a/audio/gateway.c +++ b/audio/gateway.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 diff --git a/audio/gateway.h b/audio/gateway.h index c21e728b..a4245909 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -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 diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 4ff30b84..9ce73345 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h index cf11d43e..13409232 100644 --- a/audio/gsta2dpsink.h +++ b/audio/gsta2dpsink.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c index d5737258..2a97b559 100644 --- a/audio/gstavdtpsink.c +++ b/audio/gstavdtpsink.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h index 333f1a4b..5ebc0fee 100644 --- a/audio/gstavdtpsink.h +++ b/audio/gstavdtpsink.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index 4cf872ce..b6b95e43 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index ba2a4741..235ad6bb 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h index 474d720a..e56fef35 100644 --- a/audio/gstrtpsbcpay.h +++ b/audio/gstrtpsbcpay.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index a686a406..d6777117 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index a88f8da5..e8ff9cf5 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 88737475..918324b2 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h index 41417507..17e89549 100644 --- a/audio/gstsbcenc.h +++ b/audio/gstsbcenc.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 80bf23a2..7b30dd2d 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h index a71aea72..eceeeea1 100644 --- a/audio/gstsbcparse.h +++ b/audio/gstsbcparse.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 7c47dbca..075e3a4d 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index 9e81af1a..c0e876fd 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/headset.c b/audio/headset.c index 6972daf9..c6d8436b 100644 --- a/audio/headset.c +++ b/audio/headset.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 diff --git a/audio/headset.h b/audio/headset.h index 67155f73..5a61bd56 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -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 diff --git a/audio/ipc.c b/audio/ipc.c index 05920648..e7b712da 100644 --- a/audio/ipc.c +++ b/audio/ipc.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/audio/ipc.h b/audio/ipc.h index 4da7d2ae..80deea57 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/audio/main.c b/audio/main.c index 75cb67e6..6c6b0d79 100644 --- a/audio/main.c +++ b/audio/main.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 diff --git a/audio/manager.c b/audio/manager.c index 1c8970d1..c45e45db 100644 --- a/audio/manager.c +++ b/audio/manager.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 diff --git a/audio/manager.h b/audio/manager.h index e740a524..4038d3b9 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -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 diff --git a/audio/module-bluetooth-sink.c b/audio/module-bluetooth-sink.c index 96b5d98f..f4eb355b 100644 --- a/audio/module-bluetooth-sink.c +++ b/audio/module-bluetooth-sink.c @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index e3238b9c..6863af56 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.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 library is free software; you can redistribute it and/or diff --git a/audio/rtp.h b/audio/rtp.h index 931b7346..690bd43a 100644 --- a/audio/rtp.h +++ b/audio/rtp.h @@ -2,7 +2,7 @@ * * BlueZ - Bluetooth protocol stack for Linux * - * Copyright (C) 2004-2007 Marcel Holtmann + * Copyright (C) 2004-2008 Marcel Holtmann * * * This library is free software; you can redistribute it and/or diff --git a/audio/sink.c b/audio/sink.c index a95c2e45..b44f8de6 100644 --- a/audio/sink.c +++ b/audio/sink.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 diff --git a/audio/sink.h b/audio/sink.h index a0cabdcd..fb9391df 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -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 diff --git a/audio/unix.c b/audio/unix.c index b45ab9a5..3b2d821c 100644 --- a/audio/unix.c +++ b/audio/unix.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 diff --git a/audio/unix.h b/audio/unix.h index 69fd3a4d..62474509 100644 --- a/audio/unix.h +++ b/audio/unix.h @@ -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 a1ddddc934657aa734ceb4bf581423647cc68060 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 Feb 2008 15:06:45 +0000 Subject: Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX --- audio/pcm_bluetooth.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 6863af56..565fa9ae 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -33,6 +33,7 @@ #include #include #include +#include #include -- cgit From e34ff8c0880d27c8bd95775a8c964408206f18d5 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 8 Feb 2008 17:43:48 +0000 Subject: Improve handling of different transports. --- audio/unix.c | 118 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 42 deletions(-) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index 3b2d821c..d81291c7 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -798,49 +798,25 @@ failed: unix_ipc_error(client, BT_GETCAPABILITIES_RSP, EIO); } -static void handle_setconfiguration_req(struct unix_client *client, - struct bt_setconfiguration_req *req) +static int handle_sco_transport(struct unix_client *client, + struct bt_setconfiguration_req *req) { - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; - struct mpeg_codec_cap mpeg_cap; - struct device *dev; - bdaddr_t bdaddr; - int err = 0; - - if (!req->access_mode) { - err = EINVAL; - goto failed; - } - - str2ba(req->device, &bdaddr); - - if (client->interface) { - g_free(client->interface); - client->interface = NULL; - } - - if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) - client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) - client->interface = g_strdup(AUDIO_SINK_INTERFACE); + client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - if (!manager_find_device(&bdaddr, NULL, FALSE)) { - if (!bacmp(&bdaddr, BDADDR_ANY)) - goto failed; - if (!manager_create_device(&bdaddr, create_cb, client)) - goto failed; - return; - } + info("config sco - device = %s access_mode = %u", req->device, + req->access_mode); - dev = manager_find_device(&bdaddr, client->interface, TRUE); - if (!dev) - dev = manager_find_device(&bdaddr, client->interface, FALSE); + return 0; +} - if (!dev) - goto failed; +static int handle_a2dp_transport(struct unix_client *client, + struct bt_setconfiguration_req *req) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct sbc_codec_cap sbc_cap; + struct mpeg_codec_cap mpeg_cap; - client->access_mode = req->access_mode; + client->interface = g_strdup(AUDIO_SINK_INTERFACE); if (client->caps) { g_slist_foreach(client->caps, (GFunc) g_free, NULL); @@ -852,7 +828,11 @@ static void handle_setconfiguration_req(struct unix_client *client, client->caps = g_slist_append(client->caps, media_transport); + info("config a2dp - device = %s access_mode = %u", req->device, + req->access_mode); + if (req->mpeg_capabilities.frequency) { + memset(&mpeg_cap, 0, sizeof(mpeg_cap)); mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; @@ -867,12 +847,12 @@ static void handle_setconfiguration_req(struct unix_client *client, media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, sizeof(mpeg_cap)); - info("config mpeg - frequency = %u channel_mode = %u " + info("codec mpeg12 - frequency = %u channel_mode = %u " "layer = %u crc = %u mpf = %u bitrate = %u", mpeg_cap.frequency, mpeg_cap.channel_mode, mpeg_cap.layer, mpeg_cap.crc, mpeg_cap.mpf, mpeg_cap.bitrate); - } else { + } else if (req->sbc_capabilities.frequency) { memset(&sbc_cap, 0, sizeof(sbc_cap)); sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; @@ -888,16 +868,70 @@ static void handle_setconfiguration_req(struct unix_client *client, media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, sizeof(sbc_cap)); - info("config sbc - frequency = %u channel_mode = %u " + info("codec sbc - frequency = %u channel_mode = %u " "allocation = %u subbands = %u blocks = %u " "bitpool = %u", sbc_cap.frequency, sbc_cap.channel_mode, sbc_cap.allocation_method, sbc_cap.subbands, sbc_cap.block_length, sbc_cap.max_bitpool); - } + } else + return -EINVAL; client->caps = g_slist_append(client->caps, media_codec); + return 0; +} + +static void handle_setconfiguration_req(struct unix_client *client, + struct bt_setconfiguration_req *req) +{ + struct device *dev; + bdaddr_t bdaddr; + int err = 0; + + if (!req->access_mode) { + err = EINVAL; + goto failed; + } + + str2ba(req->device, &bdaddr); + + if (client->interface) { + g_free(client->interface); + client->interface = NULL; + } + + if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) { + err = handle_sco_transport(client, req); + if (err < 0) { + err = -err; + goto failed; + } + } else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { + err = handle_a2dp_transport(client, req); + if (err < 0) { + err = -err; + goto failed; + } + } + + if (!manager_find_device(&bdaddr, NULL, FALSE)) { + if (!bacmp(&bdaddr, BDADDR_ANY)) + goto failed; + if (!manager_create_device(&bdaddr, create_cb, client)) + goto failed; + return; + } + + dev = manager_find_device(&bdaddr, client->interface, TRUE); + if (!dev) + dev = manager_find_device(&bdaddr, client->interface, FALSE); + + if (!dev) + goto failed; + + client->access_mode = req->access_mode; + start_config(dev, client); return; -- cgit From 575495ab453739d6b237d98d3c6ac86f047135ea Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 14 Feb 2008 21:07:47 +0000 Subject: Check for RFCOMM connection state in SCO connect callback (so we don't incorrectly go to CONNECTED state) --- audio/headset.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c6d8436b..8747b744 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -574,7 +574,10 @@ failed: g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); g_slist_free(hs->pending); hs->pending = NULL; - headset_set_state(device, HEADSET_STATE_CONNECTED); + if (hs->rfcomm) + headset_set_state(device, HEADSET_STATE_CONNECTED); + else + headset_set_state(device, HEADSET_STATE_DISCONNECTED); return FALSE; } -- cgit From 8fbc804e08629e5450d2641b4ba660f111dedbb2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 14 Feb 2008 21:22:38 +0000 Subject: Make use of parameters selected in alsa plugin and fix auto selection. --- audio/gstavdtpsink.c | 100 +++++++++--------------- audio/gstsbcenc.c | 13 ++-- audio/gstsbcutil.c | 8 -- audio/ipc.h | 2 - audio/pcm_bluetooth.c | 206 +++++++++++++++++++++++++++----------------------- 5 files changed, 156 insertions(+), 173 deletions(-) (limited to 'audio') diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c index 2a97b559..8c8a7465 100644 --- a/audio/gstavdtpsink.c +++ b/audio/gstavdtpsink.c @@ -279,9 +279,7 @@ static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink, value = gst_structure_get_value(structure, "mode"); pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; - else if (strcmp(pref, "mono") == 0) + if (strcmp(pref, "mono") == 0) cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; else if (strcmp(pref, "dual") == 0) cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; @@ -296,9 +294,7 @@ static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink, value = gst_structure_get_value(structure, "allocation"); pref = g_value_get_string(value); - if (strcmp(pref, "auto") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_AUTO; - else if (strcmp(pref, "loudness") == 0) + if (strcmp(pref, "loudness") == 0) cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; else if (strcmp(pref, "snr") == 0) cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; @@ -438,32 +434,21 @@ static GstStructure *gst_avdtp_sink_parse_sbc_caps( /* mode */ list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { - g_value_set_static_string(value, "joint"); + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + g_value_set_static_string(value, "mono"); gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { g_value_set_static_string(value, "stereo"); gst_value_list_prepend_value(list, value); - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { g_value_set_static_string(value, "dual"); gst_value_list_prepend_value(list, value); - } else { - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } + } + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { + g_value_set_static_string(value, "joint"); + gst_value_list_prepend_value(list, value); } g_value_unset(value); if (list) { @@ -519,20 +504,13 @@ static GstStructure *gst_avdtp_sink_parse_sbc_caps( /* allocation */ g_value_init(value, G_TYPE_STRING); list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method == BT_A2DP_ALLOCATION_AUTO) { + if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { g_value_set_static_string(value, "loudness"); gst_value_list_prepend_value(list, value); + } + if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { g_value_set_static_string(value, "snr"); gst_value_list_prepend_value(list, value); - } else { - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } } g_value_unset(value); if (list) { @@ -576,37 +554,33 @@ static GstStructure *gst_avdtp_sink_parse_sbc_caps( g_value_unset(value); /* channels */ - if (sbc->channel_mode == BT_A2DP_CHANNEL_MODE_AUTO) { + mono = FALSE; + stereo = FALSE; + if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + mono = TRUE; + if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || + (sbc->channel_mode & + BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) + stereo = TRUE; + + if (mono && stereo) { g_value_init(value, GST_TYPE_INT_RANGE); gst_value_set_int_range(value, 1, 2); } else { - mono = FALSE; - stereo = FALSE; - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } + g_value_init(value, G_TYPE_INT); + if (mono) + g_value_set_int(value, 1); + else if (stereo) + g_value_set_int(value, 2); + else { + GST_ERROR_OBJECT(self, + "Unexpected number of channels"); + g_value_set_int(value, 0); } } + gst_structure_set_value(structure, "channels", value); g_free(value); diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 918324b2..7777084c 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -31,10 +31,10 @@ #include "gstsbcenc.h" #include "gstsbcutil.h" -#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_AUTO +#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_JOINT_STEREO #define SBC_ENC_DEFAULT_BLOCKS 0 #define SBC_ENC_DEFAULT_SUB_BANDS 0 -#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_AUTO +#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_LOUDNESS #define SBC_ENC_DEFAULT_RATE 0 #define SBC_ENC_DEFAULT_CHANNELS 0 @@ -73,7 +73,6 @@ static GType gst_sbc_allocation_get_type(void) { static GType sbc_allocation_type = 0; static GEnumValue sbc_allocations[] = { - { BT_A2DP_ALLOCATION_AUTO, "Auto", "auto" }, { BT_A2DP_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, { BT_A2DP_ALLOCATION_SNR, "SNR", "snr" }, { -1, NULL, NULL} @@ -201,7 +200,7 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) gst_sbc_util_set_structure_int_param(structure, "bitpool", enc->bitpool, value); - if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO) { + if (enc->mode != SBC_ENC_DEFAULT_MODE) { enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); enum_value = g_enum_get_value(enum_class, enc->mode); gst_sbc_util_set_structure_string_param(structure, "mode", @@ -209,7 +208,7 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) g_type_class_unref(enum_class); } - if (enc->allocation != BT_A2DP_ALLOCATION_AUTO) { + if (enc->allocation != SBC_ENC_DEFAULT_ALLOCATION) { enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); enum_value = g_enum_get_value(enum_class, enc->allocation); gst_sbc_util_set_structure_string_param(structure, "allocation", @@ -341,10 +340,10 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) goto fail; mode = gst_sbc_get_mode_int_from_sbc_t(&enc->sbc); - if (enc->mode != BT_A2DP_CHANNEL_MODE_AUTO && mode != enc->mode) + if (enc->mode != SBC_ENC_DEFAULT_MODE && mode != enc->mode) goto fail; - if (enc->allocation != BT_A2DP_ALLOCATION_AUTO && + if (enc->allocation != SBC_ENC_DEFAULT_ALLOCATION && enc->sbc.allocation != enc->allocation) goto fail; diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index 075e3a4d..de48838f 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -131,8 +131,6 @@ gint gst_sbc_get_allocation_mode_int(const gchar *allocation) return BT_A2DP_ALLOCATION_LOUDNESS; else if (g_ascii_strcasecmp(allocation, "snr") == 0) return BT_A2DP_ALLOCATION_SNR; - else if (g_ascii_strcasecmp(allocation, "auto") == 0) - return BT_A2DP_ALLOCATION_AUTO; else return -1; } @@ -147,8 +145,6 @@ gint gst_sbc_get_mode_int(const gchar *mode) return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; else if (g_ascii_strcasecmp(mode, "mono") == 0) return BT_A2DP_CHANNEL_MODE_MONO; - else if (g_ascii_strcasecmp(mode, "auto") == 0) - return BT_A2DP_CHANNEL_MODE_AUTO; else return -1; } @@ -190,8 +186,6 @@ const gchar *gst_sbc_get_mode_string(gint joint) return "stereo"; case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: return "joint"; - case BT_A2DP_CHANNEL_MODE_AUTO: - return NULL; /* TODO what should be selected here? */ default: return NULL; } @@ -204,8 +198,6 @@ const gchar *gst_sbc_get_allocation_string(gint alloc) return "loudness"; case BT_A2DP_ALLOCATION_SNR: return "snr"; - case BT_A2DP_ALLOCATION_AUTO: - return "loudness"; /* TODO what should be selected here? */ default: return NULL; } diff --git a/audio/ipc.h b/audio/ipc.h index 80deea57..c900fcd1 100644 --- a/audio/ipc.h +++ b/audio/ipc.h @@ -141,7 +141,6 @@ struct bt_getcapabilities_req { #define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) #define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1) #define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1 -#define BT_A2DP_CHANNEL_MODE_AUTO 0 #define BT_A2DP_BLOCK_LENGTH_4 (1 << 3) #define BT_A2DP_BLOCK_LENGTH_8 (1 << 2) @@ -153,7 +152,6 @@ struct bt_getcapabilities_req { #define BT_A2DP_ALLOCATION_SNR (1 << 1) #define BT_A2DP_ALLOCATION_LOUDNESS 1 -#define BT_A2DP_ALLOCATION_AUTO 0 #define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5) #define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4) diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 565fa9ae..ef12b400 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -503,10 +503,16 @@ static uint8_t default_bitpool(uint8_t freq, uint8_t mode) } } -static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, - unsigned int channels) +static int bluetooth_a2dp_init(struct bluetooth_data *data, + snd_pcm_hw_params_t *params) { - unsigned int max_bitpool, min_bitpool; + struct bluetooth_alsa_config *cfg = &data->alsa_config; + sbc_capabilities_t *cap = &data->a2dp.sbc_capabilities; + unsigned int max_bitpool, min_bitpool, rate, channels; + int dir; + + snd_pcm_hw_params_get_rate(params, &rate, &dir); + snd_pcm_hw_params_get_channels(params, &channels); switch (rate) { case 48000: @@ -526,7 +532,9 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, return -1; } - if (channels == 2) { + if (cfg->has_channel_mode) + cap->channel_mode = cfg->channel_mode; + else if (channels == 2) { if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) @@ -543,7 +551,9 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, return -1; } - if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) + if (cfg->has_block_length) + cap->block_length = cfg->block_length; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) cap->block_length = BT_A2DP_BLOCK_LENGTH_16; else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) cap->block_length = BT_A2DP_BLOCK_LENGTH_12; @@ -556,6 +566,8 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, return -1; } + if (cfg->has_subbands) + cap->subbands = cfg->subbands; if (cap->subbands & BT_A2DP_SUBBANDS_8) cap->subbands = BT_A2DP_SUBBANDS_8; else if (cap->subbands & BT_A2DP_SUBBANDS_4) @@ -565,14 +577,21 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, return -1; } + if (cfg->has_allocation_method) + cap->allocation_method = cfg->allocation_method; if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) cap->allocation_method = BT_A2DP_ALLOCATION_SNR; - min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool); - max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), - cap->max_bitpool); + if (cfg->has_bitpool) + min_bitpool = max_bitpool = cfg->bitpool; + else { + min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool); + max_bitpool = MIN(default_bitpool(cap->frequency, + cap->channel_mode), + cap->max_bitpool); + } cap->min_bitpool = min_bitpool; cap->max_bitpool = max_bitpool; @@ -580,63 +599,10 @@ static int select_sbc_params(sbc_capabilities_t *cap, unsigned int rate, return 0; } - -static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params) +static void bluetooth_a2dp_setup(struct bluetooth_a2dp *a2dp) { - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; - char buf[BT_AUDIO_IPC_PACKET_SIZE]; - bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; - struct bt_setconfiguration_req *setconf_req = (void*) buf; - struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; - unsigned int rate, channels; - int err, dir; - sbc_capabilities_t active_capabilities; + sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities; - DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", - io->period_size, io->buffer_size); - - /* FIXME: this needs to be really implemented (take into account - real asoundrc settings + ALSA hw settings ) once server side sends us - more than one possible configuration */ - snd_pcm_hw_params_get_rate(params, &rate, &dir); - snd_pcm_hw_params_get_channels(params, &channels); - err = select_sbc_params(&a2dp->sbc_capabilities, rate, channels); - if (err < 0) - return err; - - active_capabilities = a2dp->sbc_capabilities; - - memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); - setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; - strncpy(setconf_req->device, data->alsa_config.device, 18); - setconf_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; - setconf_req->sbc_capabilities = active_capabilities; - setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? - BT_CAPABILITIES_ACCESS_MODE_WRITE : - BT_CAPABILITIES_ACCESS_MODE_READ); - - err = audioservice_send(data->server.fd, &setconf_req->h); - if (err < 0) - return err; - - err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, - BT_SETCONFIGURATION_RSP); - if (err < 0) - return err; - - if (rsp_hdr->posix_errno != 0) { - SNDERR("BT_SETCONFIGURATION failed : %s(%d)", - strerror(rsp_hdr->posix_errno), - rsp_hdr->posix_errno); - return -rsp_hdr->posix_errno; - } - - data->transport = setconf_rsp->transport; - data->link_mtu = setconf_rsp->link_mtu; - - /* Setup SBC encoder now we agree on parameters */ if (a2dp->sbc_initialized) sbc_reinit(&a2dp->sbc, 0); else @@ -695,9 +661,58 @@ static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, } a2dp->sbc.bitpool = active_capabilities.max_bitpool; - a2dp->codesize = a2dp->sbc.subbands * a2dp->sbc.blocks * - a2dp->sbc.channels * 2; + a2dp->codesize = sbc_get_codesize(&a2dp->sbc); a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); +} + +static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, + snd_pcm_hw_params_t *params) +{ + struct bluetooth_data *data = io->private_data; + struct bluetooth_a2dp *a2dp = &data->a2dp; + char buf[BT_AUDIO_IPC_PACKET_SIZE]; + bt_audio_rsp_msg_header_t *rsp_hdr = (void*) buf; + struct bt_setconfiguration_req *setconf_req = (void*) buf; + struct bt_setconfiguration_rsp *setconf_rsp = (void*) buf; + int err; + + DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", + io->period_size, io->buffer_size); + + err = bluetooth_a2dp_init(data, params); + if (err < 0) + return err; + + memset(setconf_req, 0, BT_AUDIO_IPC_PACKET_SIZE); + setconf_req->h.msg_type = BT_SETCONFIGURATION_REQ; + strncpy(setconf_req->device, data->alsa_config.device, 18); + setconf_req->transport = BT_CAPABILITIES_TRANSPORT_A2DP; + setconf_req->sbc_capabilities = a2dp->sbc_capabilities; + setconf_req->access_mode = (io->stream == SND_PCM_STREAM_PLAYBACK ? + BT_CAPABILITIES_ACCESS_MODE_WRITE : + BT_CAPABILITIES_ACCESS_MODE_READ); + + err = audioservice_send(data->server.fd, &setconf_req->h); + if (err < 0) + return err; + + err = audioservice_expect(data->server.fd, &rsp_hdr->msg_h, + BT_SETCONFIGURATION_RSP); + if (err < 0) + return err; + + if (rsp_hdr->posix_errno != 0) { + SNDERR("BT_SETCONFIGURATION failed : %s(%d)", + strerror(rsp_hdr->posix_errno), + rsp_hdr->posix_errno); + return -rsp_hdr->posix_errno; + } + + data->transport = setconf_rsp->transport; + data->link_mtu = setconf_rsp->link_mtu; + + /* Setup SBC encoder now we agree on parameters */ + bluetooth_a2dp_setup(a2dp); DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, @@ -1172,6 +1187,7 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) { struct bluetooth_data *data = io->private_data; struct bluetooth_a2dp *a2dp = &data->a2dp; + struct bluetooth_alsa_config *cfg = &data->alsa_config; snd_pcm_access_t access_list[] = { SND_PCM_ACCESS_RW_INTERLEAVED, /* Mmap access is really useless fo this driver, but we @@ -1204,12 +1220,17 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) return err; /* supported channels */ - if (a2dp->sbc_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) + if (cfg->has_channel_mode) + a2dp->sbc_capabilities.channel_mode = cfg->channel_mode; + + if (a2dp->sbc_capabilities.channel_mode & + BT_A2DP_CHANNEL_MODE_MONO) min_channels = 1; else min_channels = 2; - if (a2dp->sbc_capabilities.channel_mode & (~BT_A2DP_CHANNEL_MODE_MONO)) + if (a2dp->sbc_capabilities.channel_mode & + (~BT_A2DP_CHANNEL_MODE_MONO)) max_channels = 2; else max_channels = 1; @@ -1234,28 +1255,33 @@ static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) /* supported rates */ rate_count = 0; - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_16000) { - rate_list[rate_count] = 16000; + if (cfg->has_rate) { + rate_list[rate_count] = cfg->rate; rate_count++; - } + } else { + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_16000) { + rate_list[rate_count] = 16000; + rate_count++; + } - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_32000) { - rate_list[rate_count] = 32000; - rate_count++; - } + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_32000) { + rate_list[rate_count] = 32000; + rate_count++; + } - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_44100) { - rate_list[rate_count] = 44100; - rate_count++; - } + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_44100) { + rate_list[rate_count] = 44100; + rate_count++; + } - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_48000) { - rate_list[rate_count] = 48000; - rate_count++; + if (a2dp->sbc_capabilities.frequency & + BT_SBC_SAMPLING_FREQ_48000) { + rate_list[rate_count] = 48000; + rate_count++; + } } err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, @@ -1348,10 +1374,7 @@ static int bluetooth_parse_config(snd_config_t *conf, return -EINVAL; } - if (strcmp(value, "auto") == 0) { - bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_AUTO; - bt_config->has_channel_mode = 1; - } else if (strcmp(value, "mono") == 0) { + if (strcmp(value, "mono") == 0) { bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; bt_config->has_channel_mode = 1; } else if (strcmp(value, "dual") == 0) { @@ -1373,10 +1396,7 @@ static int bluetooth_parse_config(snd_config_t *conf, return -EINVAL; } - if (strcmp(value, "auto") == 0) { - bt_config->allocation_method = BT_A2DP_ALLOCATION_AUTO; - bt_config->has_allocation_method = 1; - } else if (strcmp(value, "loudness") == 0) { + if (strcmp(value, "loudness") == 0) { bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; bt_config->has_allocation_method = 1; } else if (strcmp(value, "snr") == 0) { -- cgit From 0a47bd96e7953af2345b9c603db90e71d51dd39c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 14 Feb 2008 22:18:30 +0000 Subject: Remove (incorrect) spaces after commans in AT strings --- audio/headset.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 8747b744..84517f1d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -196,7 +196,7 @@ static int report_indicators(struct device *device, const char *buf) err = headset_send(hs, "\r\n+CIND:(\"service\",(0,1))," "(\"call\",(0,1)),(\"callsetup\",(0-3))\r\n"); else - err = headset_send(hs, "\r\n+CIND:1, 0, 0\r\n"); + err = headset_send(hs, "\r\n+CIND:1,0,0\r\n"); if (err < 0) return err; @@ -249,12 +249,12 @@ static int answer_call(struct device *device, const char *buf) return err; /*+CIEV: (call = 1)*/ - err = headset_send(hs, "\r\n+CIEV:2, 1\r\n"); + err = headset_send(hs, "\r\n+CIEV:2,1\r\n"); if (err < 0) return err; /*+CIEV: (callsetup = 0)*/ - return headset_send(hs, "\r\n+CIEV:3, 0\r\n"); + return headset_send(hs, "\r\n+CIEV:3,0\r\n"); } static int terminate_call(struct device *device, const char *buf) @@ -279,11 +279,11 @@ static int terminate_call(struct device *device, const char *buf) g_source_remove(hs->ring_timer); hs->ring_timer = 0; /*+CIEV: (callsetup = 0)*/ - return headset_send(hs, "\r\n+CIEV:3, 0\r\n"); + return headset_send(hs, "\r\n+CIEV:3,0\r\n"); } /*+CIEV: (call = 0)*/ - return headset_send(hs, "\r\n+CIEV:2, 0\r\n"); + return headset_send(hs, "\r\n+CIEV:2,0\r\n"); } static int cli_notification(struct device *device, const char *buf) @@ -1220,7 +1220,7 @@ static gboolean ring_timer_cb(gpointer data) strerror(-err), -err); if (hs->cli_active && hs->ph_number) { - err = headset_send(hs, "\r\n+CLIP:\"%s\", %d\r\n", + err = headset_send(hs, "\r\n+CLIP:\"%s\",%d\r\n", hs->ph_number, hs->type); if (err < 0) @@ -1258,7 +1258,7 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, } if (hs->cli_active && hs->ph_number) { - err = headset_send(hs, "\r\n+CLIP:\"%s\", %d\r\n", + err = headset_send(hs, "\r\n+CLIP:\"%s\",%d\r\n", hs->ph_number, hs->type); if (err < 0) { dbus_message_unref(reply); @@ -1301,7 +1301,7 @@ done: if (hs->hfp_active) { int err; /*+CIEV: (callsetup = 0)*/ - err = headset_send(hs, "\r\n+CIEV:3, 0\r\n"); + err = headset_send(hs, "\r\n+CIEV:3,0\r\n"); if (err < 0) { dbus_message_unref(reply); return error_failed_errno(conn, msg, -err); @@ -1503,11 +1503,11 @@ static DBusHandlerResult hf_setup_call(DBusConnection *conn, return DBUS_HANDLER_RESULT_NEED_MEMORY; if (!strncmp(value, "incoming", 8)) - err = headset_send(hs, "\r\n+CIEV:3, 1\r\n"); + err = headset_send(hs, "\r\n+CIEV:3,1\r\n"); else if (!strncmp(value, "outgoing", 8)) - err = headset_send(hs, "\r\n+CIEV:3, 2\r\n"); + err = headset_send(hs, "\r\n+CIEV:3,2\r\n"); else if (!strncmp(value, "remote", 6)) - err = headset_send(hs, "\r\n+CIEV:3, 3\r\n"); + err = headset_send(hs, "\r\n+CIEV:3,3\r\n"); else err = -EINVAL; -- cgit From c5463ab43438de912b5fd642ec8e248f1e9683dd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 18 Feb 2008 14:17:33 +0000 Subject: Cleanup headset connection logic to accommodate necessary HFP fixes --- audio/headset.c | 557 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 296 insertions(+), 261 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 84517f1d..bfad8455 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -87,15 +87,19 @@ static char *str_state[] = { "HEADSET_STATE_PLAYING", }; +struct connect_cb { + int id; + headset_stream_cb_t cb; + void *cb_data; +}; + struct pending_connect { DBusMessage *msg; DBusPendingCall *call; GIOChannel *io; - int sock; int err; - unsigned int id; - headset_stream_cb_t cb; - void *cb_data; + headset_state_t target_state; + GSList *callbacks; }; struct headset { @@ -121,7 +125,7 @@ struct headset { int type; headset_state_t state; - GSList *pending; + struct pending_connect *pending; int sp_gain; int mic_gain; @@ -135,8 +139,10 @@ struct event { int (*callback) (struct device *device, const char *buf); }; -static int rfcomm_connect(struct device *device, struct pending_connect *c); -static int get_handles(struct device *device, struct pending_connect *c); +static int rfcomm_connect(struct device *device, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id); +static int get_handles(struct device *device, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id); static int headset_send(struct headset *hs, char *format, ...) { @@ -152,7 +158,7 @@ static int headset_send(struct headset *hs, char *format, ...) if (count < 0) return -EINVAL; - if (hs->state < HEADSET_STATE_CONNECTED || !hs->rfcomm) { + if (!hs->rfcomm) { error("headset_send: the headset is not connected"); return -EIO; } @@ -370,22 +376,6 @@ static int handle_event(struct device *device, const char *buf) return -EINVAL; } -static void pending_connect_free(struct pending_connect *c) -{ - if (c->io) { - g_io_channel_close(c->io); - g_io_channel_unref(c->io); - } - if (c->msg) - dbus_message_unref(c->msg); - if (c->call) { - dbus_pending_call_cancel(c->call); - dbus_pending_call_unref(c->call); - } - - g_free(c); -} - static void close_sco(struct device *device) { struct headset *hs = device->headset; @@ -485,72 +475,104 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static void pending_connect_ok(struct pending_connect *c, struct device *dev) +static void pending_connect_complete(struct connect_cb *cb, struct device *dev) +{ + struct headset *hs = dev->headset; + + if (hs->pending->err) + cb->cb(NULL, cb->cb_data); + else + cb->cb(dev, cb->cb_data); +} + +static void pending_connect_finalize(struct device *dev) { struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; + + g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev); - if (c->msg) { - DBusMessage *reply = dbus_message_new_method_return(c->msg); - if (reply) - send_message_and_unref(dev->conn, reply); + g_slist_foreach(p->callbacks, (GFunc) g_free, NULL); + g_slist_free(p->callbacks); + + if (p->io) { + g_io_channel_close(p->io); + g_io_channel_unref(p->io); } - if (c->cb) { - if (hs->rfcomm && hs->sco) - c->cb(dev, c->cb_data); - else - c->cb(NULL, c->cb_data); + if (p->msg) + dbus_message_unref(p->msg); + + if (p->call) { + dbus_pending_call_cancel(p->call); + dbus_pending_call_unref(p->call); } - pending_connect_free(c); + g_free(p); + + hs->pending = NULL; } -static gboolean finalize_stream_setup(struct device *dev) +static void pending_connect_init(struct headset *hs, headset_state_t target_state) { - struct headset *hs = dev->headset; - - g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, dev); - g_slist_free(hs->pending); - hs->pending = NULL; + if (hs->pending) { + if (hs->pending->target_state < target_state) + hs->pending->target_state = target_state; + return; + } - return FALSE; + hs->pending = g_new0(struct pending_connect, 1); + hs->pending->target_state = target_state; } -static void pending_connect_failed(struct pending_connect *c, struct device *dev) +static unsigned int connect_cb_new(struct headset *hs, + headset_state_t target_state, + headset_stream_cb_t func, + void *user_data) { - if (c->msg) - error_connection_attempt_failed(dev->conn, c->msg, c->err); - if (c->cb) - c->cb(NULL, c->cb_data); - pending_connect_free(c); + struct connect_cb *cb; + unsigned int free_cb_id = 1; + + pending_connect_init(hs, target_state); + + cb = g_new(struct connect_cb, 1); + + cb->cb = func; + cb->cb_data = user_data; + cb->id = free_cb_id++; + + hs->pending->callbacks = g_slist_append(hs->pending->callbacks, + cb); + + return cb->id; } static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *device) { struct headset *hs; - struct pending_connect *c; int ret, sk; socklen_t len; + struct pending_connect *p; if (cond & G_IO_NVAL) return FALSE; hs = device->headset; - c = hs->pending->data; + p = hs->pending; sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - c->err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err), - c->err); + p->err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), + p->err); goto failed; } if (ret != 0) { - c->err = ret; + p->err = ret; error("connect(): %s (%d)", strerror(ret), ret); goto failed; } @@ -559,11 +581,10 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, info("SCO fd=%d", sk); hs->sco = chan; - c->io = NULL; + p->io = NULL; + + pending_connect_finalize(device); - g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device); - g_slist_free(hs->pending); - hs->pending = NULL; fcntl(sk, F_SETFL, 0); headset_set_state(device, HEADSET_STATE_PLAYING); @@ -571,9 +592,7 @@ static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; failed: - g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); - g_slist_free(hs->pending); - hs->pending = NULL; + pending_connect_finalize(device); if (hs->rfcomm) headset_set_state(device, HEADSET_STATE_CONNECTED); else @@ -582,18 +601,16 @@ failed: return FALSE; } -static int sco_connect(struct device *device, struct pending_connect *c) +static int sco_connect(struct device *dev, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id) { - struct headset *hs = device->headset; + struct headset *hs = dev->headset; struct sockaddr_sco addr; - gboolean do_callback = FALSE; + GIOChannel *io; int sk, err; - if (!g_slist_find(hs->pending, c)) - hs->pending = g_slist_append(hs->pending, c); - if (hs->state != HEADSET_STATE_CONNECTED) - return 0; + return -EINVAL; sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); if (sk < 0) { @@ -602,10 +619,10 @@ static int sco_connect(struct device *device, struct pending_connect *c) return -err; } - c->io = g_io_channel_unix_new(sk); - if (!c->io) { + io = g_io_channel_unix_new(sk); + if (!io) { close(sk); - return -EINVAL; + return -ENOMEM; } memset(&addr, 0, sizeof(addr)); @@ -615,46 +632,55 @@ static int sco_connect(struct device *device, struct pending_connect *c) if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = errno; error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - return -err; + goto failed; } if (set_nonblocking(sk) < 0) { err = errno; - return -err; + goto failed; } memset(&addr, 0, sizeof(addr)); addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &device->dst); + bacpy(&addr.sco_bdaddr, &dev->dst); - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - if (!(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect: %s (%d)", strerror(errno), errno); - return -err; - } + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); - g_io_add_watch(c->io, - G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, device); - } else - do_callback = TRUE; + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect: %s (%d)", strerror(errno), errno); + goto failed; + } + + headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS); + + pending_connect_init(hs, HEADSET_STATE_PLAYING); + + if (cb) { + unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING, + cb, user_data); + if (cb_id) + *cb_id = id; + } - headset_set_state(device, HEADSET_STATE_PLAY_IN_PROGRESS); - if (!g_slist_find(hs->pending, c)) - hs->pending = g_slist_append(hs->pending, c); + g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + (GIOFunc) sco_connect_cb, dev); - if (do_callback) - sco_connect_cb(c->io, G_IO_OUT, device); + hs->pending->io = io; return 0; + +failed: + g_io_channel_close(io); + g_io_channel_unref(io); + return -err; } static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, - struct device *device) + struct device *dev) { struct headset *hs; - struct pending_connect *c; + struct pending_connect *p; char hs_address[18]; int sk, ret; socklen_t len; @@ -662,62 +688,59 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, if (cond & G_IO_NVAL) return FALSE; - hs = device->headset; - c = hs->pending->data; + hs = dev->headset; + p = hs->pending; sk = g_io_channel_unix_get_fd(chan); len = sizeof(ret); if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - c->err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(c->err), c->err); + p->err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), p->err); goto failed; } if (ret != 0) { - c->err = ret; + p->err = ret; error("connect(): %s (%d)", strerror(ret), ret); goto failed; } - ba2str(&device->dst, hs_address); + ba2str(&dev->dst, hs_address); hs->rfcomm = chan; - c->io = NULL; + p->io = NULL; if (server_is_enabled(HANDSFREE_SVCLASS_ID) && hs->hfp_handle != 0) hs->hfp_active = TRUE; else hs->hfp_active = FALSE; - headset_set_state(device, HEADSET_STATE_CONNECTED); + headset_set_state(dev, HEADSET_STATE_CONNECTED); - debug("%s: Connected to %s", device->path, hs_address); + debug("%s: Connected to %s", dev->path, hs_address); g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, device); + (GIOFunc) rfcomm_io_cb, dev); - if (c->cb) { - if (sco_connect(device, c) < 0) { - c->err = EIO; + if (p->target_state == HEADSET_STATE_PLAYING) { + p->err = sco_connect(dev, NULL, NULL, NULL); + if (p->err < 0) goto failed; - } return FALSE; } - g_slist_foreach(hs->pending, (GFunc) pending_connect_ok, device); - g_slist_free(hs->pending); - hs->pending = NULL; + pending_connect_finalize(dev); return FALSE; failed: - g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); - g_slist_free(hs->pending); - hs->pending = NULL; + if (p->msg) + error_connection_attempt_failed(dev->conn, p->msg, p->err); + pending_connect_finalize(dev); if (hs->rfcomm) - headset_set_state(device, HEADSET_STATE_CONNECTED); + headset_set_state(dev, HEADSET_STATE_CONNECTED); else - headset_set_state(device, HEADSET_STATE_DISCONNECTED); + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return FALSE; } @@ -731,11 +754,9 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; - struct device *device = data; - struct headset *hs = device->headset; - struct pending_connect *c; - - c = hs->pending->data; + struct device *dev = data; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; reply = dbus_pending_call_steal_reply(call); @@ -812,10 +833,11 @@ static void get_record_reply(DBusPendingCall *call, void *data) hs->rfcomm_ch = ch; - err = rfcomm_connect(device, NULL); + err = rfcomm_connect(dev, NULL, NULL, NULL); if (err < 0) { error("Unable to connect: %s (%s)", strerror(-err), -err); - c->err = -err; + p->err = -err; + error_connection_attempt_failed(dev->conn, p->msg, p->err); goto failed; } @@ -823,15 +845,15 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_record_free(record); dbus_message_unref(reply); - device_finish_sdp_transaction(device); + device_finish_sdp_transaction(dev); return; failed_not_supported: - if (c->msg) { - error_not_supported(device->conn, c->msg); - dbus_message_unref(c->msg); - c->msg = NULL; + if (p->msg) { + error_not_supported(dev->conn, p->msg); + dbus_message_unref(p->msg); + p->msg = NULL; } failed: if (classes) @@ -840,11 +862,9 @@ failed: sdp_record_free(record); if (reply) dbus_message_unref(reply); - g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); - g_slist_free(hs->pending); - hs->pending = NULL; - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - device_finish_sdp_transaction(device); + pending_connect_finalize(dev); + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + device_finish_sdp_transaction(dev); } static void get_handles_reply(DBusPendingCall *call, void *data) @@ -852,28 +872,26 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBusMessage *msg = NULL, *reply; DBusPendingCall *pending; DBusError derr; - struct device *device = data; - struct headset *hs = device->headset; - struct pending_connect *c; + struct device *dev = data; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; char address[18], *addr_ptr = address; dbus_uint32_t *array = NULL; dbus_uint32_t handle; int array_len; - c = hs->pending->data; - reply = dbus_pending_call_steal_reply(call); dbus_error_init(&derr); if (dbus_set_error_from_message(&derr, reply)) { error("GetRemoteServiceHandles failed: %s", derr.message); - if (c->msg) { + if (p->msg) { if (dbus_error_has_name(&derr, "org.bluez.Error.ConnectionAttemptFailed")) - error_connection_attempt_failed(device->conn, c->msg, - EHOSTDOWN); + error_connection_attempt_failed(dev->conn, p->msg, + EHOSTDOWN); else - error_not_supported(device->conn, c->msg); + error_not_supported(dev->conn, p->msg); } dbus_error_free(&derr); goto failed; @@ -886,15 +904,15 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_INVALID)) { error("Unable to get args from reply: %s", derr.message); dbus_error_free(&derr); - if (c->msg) - error_not_supported(device->conn, c->msg); + if (p->msg) + error_not_supported(dev->conn, p->msg); goto failed; } if (!array) { error("get_handles_reply: Unable to get handle array from reply"); - if (c->msg) - error_not_supported(device->conn, c->msg); + if (p->msg) + error_not_supported(dev->conn, p->msg); goto failed; } @@ -902,32 +920,32 @@ static void get_handles_reply(DBusPendingCall *call, void *data) if (hs->search_hfp) { debug("No record handles found for hfp"); hs->search_hfp = FALSE; - get_handles(device, c); + get_handles(dev, NULL, NULL, NULL); dbus_message_unref(reply); return; } debug("No record handles found for hsp"); - if (c->msg) - error_not_supported(device->conn, c->msg); + if (p->msg) + error_not_supported(dev->conn, p->msg); goto failed; } if (array_len > 1) debug("Multiple records found. Using the first one."); - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, + msg = dbus_message_new_method_call("org.bluez", dev->adapter_path, "org.bluez.Adapter", "GetRemoteServiceRecord"); if (!msg) { error("Unable to allocate new method call"); - if (c->msg) - error_out_of_memory(device->conn, c->msg); + if (p->msg) + error_out_of_memory(dev->conn, p->msg); goto failed; } - ba2str(&device->dst, address); + ba2str(&dev->dst, address); handle = array[0]; @@ -935,14 +953,14 @@ static void get_handles_reply(DBusPendingCall *call, void *data) DBUS_TYPE_UINT32, &handle, DBUS_TYPE_INVALID); - if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { + if (!dbus_connection_send_with_reply(dev->conn, msg, &pending, -1)) { error("Sending GetRemoteServiceRecord failed"); - if (c->msg) - error_connection_attempt_failed(device->conn, c->msg, EIO); + if (p->msg) + error_connection_attempt_failed(dev->conn, p->msg, EIO); goto failed; } - dbus_pending_call_set_notify(pending, get_record_reply, device, NULL); + dbus_pending_call_set_notify(pending, get_record_reply, dev, NULL); dbus_pending_call_unref(pending); dbus_message_unref(msg); dbus_message_unref(reply); @@ -952,19 +970,14 @@ static void get_handles_reply(DBusPendingCall *call, void *data) failed: if (msg) dbus_message_unref(msg); - /* The reply was already sent above */ - if (c->msg) { - dbus_message_unref(c->msg); - c->msg = NULL; - } dbus_message_unref(reply); - g_slist_foreach(hs->pending, (GFunc) pending_connect_failed, device); - g_slist_free(hs->pending); - hs->pending = NULL; - headset_set_state(device, HEADSET_STATE_DISCONNECTED); + p->err = EIO; + pending_connect_finalize(dev); + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); } -static int get_handles(struct device *device, struct pending_connect *c) +static int get_handles(struct device *device, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id) { DBusPendingCall *pending; struct headset *hs = device->headset; @@ -978,7 +991,7 @@ static int get_handles(struct device *device, struct pending_connect *c) "GetRemoteServiceHandles"); if (!msg) { error("Could not create a new dbus message"); - return -EINVAL; + return -ENOMEM; } if (hs->search_hfp) @@ -999,45 +1012,56 @@ static int get_handles(struct device *device, struct pending_connect *c) return -EIO; } + pending_connect_init(hs, HEADSET_STATE_CONNECTED); + + if (cb) { + unsigned int id; + id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, + cb, user_data); + if (cb_id) + *cb_id = id; + } + dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); - if (c) - c->call = pending; + + if (hs->pending) + hs->pending->call = pending; else dbus_pending_call_unref(pending); + dbus_message_unref(msg); return 0; } -static int rfcomm_connect(struct device *device, struct pending_connect *c) +static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id) { - struct headset *hs = device->headset; + struct headset *hs = dev->headset; struct sockaddr_rc addr; + GIOChannel *io; char address[18]; int sk, err; - if (c != NULL) { - if (!g_slist_find(hs->pending, c)) - hs->pending = g_slist_append(hs->pending, c); + if (hs->rfcomm_ch < 0) + return get_handles(dev, cb, user_data, cb_id); - if (hs->state == HEADSET_STATE_DISCONNECTED) - return get_handles(device, c); - else - return 0; - } - else - c = hs->pending->data; - - ba2str(&device->dst, address); + ba2str(&dev->dst, address); - debug("%s: Connecting to %s channel %d", device->path, address, + debug("%s: Connecting to %s channel %d", dev->path, address, hs->rfcomm_ch); sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sk < 0) { err = errno; - error("socket: %s (%d)", strerror(err), err); - goto failed; + error("socket(BTPROTO_RFCOMM): %s (%d)", strerror(err), err); + return -err; + } + + io = g_io_channel_unix_new(sk); + if (!io) { + close(sk); + return -ENOMEM; } memset(&addr, 0, sizeof(addr)); @@ -1058,34 +1082,38 @@ static int rfcomm_connect(struct device *device, struct pending_connect *c) memset(&addr, 0, sizeof(addr)); addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &device->dst); + bacpy(&addr.rc_bdaddr, &dev->dst); addr.rc_channel = hs->rfcomm_ch; - c->io = g_io_channel_unix_new(sk); - if (!c->io) { - err = ENOMEM; - error("channel_unix_new failed in rfcomm connect"); + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect() failed: %s (%d)", strerror(err), err); goto failed; } - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - if (!(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect() failed: %s (%d)", strerror(err), err); - goto failed; - } + headset_set_state(dev, HEADSET_STATE_CONNECT_IN_PROGRESS); - g_io_add_watch(c->io, G_IO_OUT | G_IO_NVAL, - (GIOFunc) rfcomm_connect_cb, device); - } else - rfcomm_connect_cb(c->io, G_IO_OUT, device); + pending_connect_init(hs, HEADSET_STATE_CONNECTED); + + if (cb) { + unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, + cb, user_data); + if (cb_id) + *cb_id = id; + } + + g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) rfcomm_connect_cb, dev); + + hs->pending->io = io; return 0; failed: - if (!c->io && sk >= 0) - close(sk); - + g_io_channel_close(io); + g_io_channel_unref(io); return -err; } @@ -1182,29 +1210,20 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, { struct device *device = data; struct headset *hs = device->headset; - struct pending_connect *c; int err; - if (hs->state > HEADSET_STATE_DISCONNECTED) + if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS) + return error_in_progress(conn, msg, "Connect in progress"); + else if (hs->state > HEADSET_STATE_CONNECT_IN_PROGRESS) return error_already_connected(conn, msg); - c = g_try_new0(struct pending_connect, 1); - if (!c) { - error("Out of memory when allocating struct pending_connect"); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - - c->msg = dbus_message_ref(msg); - - err = rfcomm_connect(device, c); + err = rfcomm_connect(device, NULL, NULL, NULL); if (err < 0) - goto error; + return error_connection_attempt_failed(conn, msg, -err); - return DBUS_HANDLER_RESULT_HANDLED; + hs->pending->msg = dbus_message_ref(msg); -error: - pending_connect_free(c); - return error_connection_attempt_failed(conn, msg, -err); + return DBUS_HANDLER_RESULT_HANDLED; } static gboolean ring_timer_cb(gpointer data) @@ -1318,26 +1337,26 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, { struct device *device = data; struct headset *hs = device->headset; - struct pending_connect *c; int err; - if (hs->state < HEADSET_STATE_CONNECTED) + switch (hs->state) { + case HEADSET_STATE_DISCONNECTED: + case HEADSET_STATE_CONNECT_IN_PROGRESS: return error_not_connected(conn, msg); - - if (hs->state >= HEADSET_STATE_PLAY_IN_PROGRESS) + case HEADSET_STATE_PLAY_IN_PROGRESS: + return error_in_progress(conn, msg, "Play in progress"); + case HEADSET_STATE_PLAYING: return error_already_connected(conn, msg); + case HEADSET_STATE_CONNECTED: + default: + break; + } - c = g_try_new0(struct pending_connect, 1); - if (!c) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - c->msg = msg ? dbus_message_ref(msg) : NULL; - - err = sco_connect(device, c); - if (err < 0) { - pending_connect_free(c); + err = sco_connect(device, NULL, NULL, NULL); + if (err < 0) return error_failed(conn, msg, strerror(-err)); - } + + hs->pending->msg = dbus_message_ref(msg); return DBUS_HANDLER_RESULT_HANDLED; } @@ -1592,7 +1611,8 @@ static DBusSignalVTable headset_signals[] = { { NULL, NULL } }; -static void headset_set_channel(struct headset *headset, sdp_record_t *record) +static void headset_set_channel(struct headset *headset, sdp_record_t *record, + uint16_t svc) { int ch; sdp_list_t *protos; @@ -1609,7 +1629,9 @@ static void headset_set_channel(struct headset *headset, sdp_record_t *record) if (ch > 0) { headset->rfcomm_ch = ch; - debug("Discovered Headset service on RFCOMM channel %d", ch); + debug("Discovered %s service on RFCOMM channel %d", + svc == HEADSET_SVCLASS_ID ? "Headset" : "Handsfree", + ch); } else error("Unable to get RFCOMM channel from Headset record"); } @@ -1649,7 +1671,7 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) return; } - headset_set_channel(headset, record); + headset_set_channel(headset, record, svc); } struct headset *headset_init(struct device *dev, sdp_record_t *record, @@ -1684,7 +1706,7 @@ struct headset *headset_init(struct device *dev, sdp_record_t *record, return NULL; } - headset_set_channel(hs, record); + headset_set_channel(hs, record, svc); register_iface: if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, @@ -1828,61 +1850,74 @@ void headset_free(struct device *dev) gboolean headset_cancel_stream(struct device *dev, unsigned int id) { struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; GSList *l; - struct pending_connect *pending = NULL; + struct connect_cb *cb = NULL; + + if (!p) + return FALSE; - for (l = hs->pending; l != NULL; l = l->next) { - struct pending_connect *tmp = l->data; + for (l = p->callbacks; l != NULL; l = l->next) { + struct connect_cb *tmp = l->data; if (tmp->id == id) { - pending = tmp; + cb = tmp; break; } } - if (!pending) + if (!cb) return FALSE; - hs->pending = g_slist_remove(hs->pending, pending); - pending_connect_free(pending); + p->callbacks = g_slist_remove(p->callbacks, cb); + g_free(cb); - if (!hs->pending) - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + if (p->callbacks || p->msg) + return TRUE; + + pending_connect_finalize(dev); + + /* FIXME: disconnecting is not correct for all scenarios, e.g. if the + * stream request came when we were already connected then we should + * stay connected after canceling the request too */ + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return TRUE; } +static gboolean dummy_connect_complete(struct device *dev) +{ + pending_connect_finalize(dev); + return FALSE; +} + unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, void *user_data) { struct headset *hs = dev->headset; - struct pending_connect *c; - static unsigned int cb_id = 0; + unsigned int id; int err; - c = g_new0(struct pending_connect, 1); - c->cb = cb; - c->cb_data = user_data; - c->id = ++cb_id; - if (hs->rfcomm && hs->sco) { - hs->pending = g_slist_append(hs->pending, c); - g_idle_add((GSourceFunc) finalize_stream_setup, dev); - return c->id; + id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); + g_idle_add((GSourceFunc) dummy_connect_complete, dev); + return id; } if (hs->rfcomm == NULL) - err = rfcomm_connect(dev, c); + err = rfcomm_connect(dev, cb, user_data, &id); else if (hs->sco == NULL) - err = sco_connect(dev, c); + err = sco_connect(dev, cb, user_data, &id); if (err < 0) goto error; - return c->id; + hs->pending->target_state = HEADSET_STATE_PLAYING; + + return id; error: - pending_connect_free(c); + pending_connect_finalize(dev); return 0; } @@ -1936,7 +1971,7 @@ void headset_set_state(struct device *dev, headset_state_t state) if (hs->state == state) return; - switch(state) { + switch (state) { case HEADSET_STATE_DISCONNECTED: close_sco(dev); headset_close_rfcomm(dev); -- cgit From 8bf41a847b9e8c768a21349659454bd2be8d1824 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 18 Feb 2008 14:32:11 +0000 Subject: Fix callback id type --- audio/headset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index bfad8455..c1d7ea24 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -88,7 +88,7 @@ static char *str_state[] = { }; struct connect_cb { - int id; + unsigned int id; headset_stream_cb_t cb; void *cb_data; }; -- cgit From b3f285d83c7dfe446bd7f2460e6f795e22a72d32 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 19 Feb 2008 08:20:22 +0000 Subject: Implement proper HFP SLC handling --- audio/headset.c | 512 ++++++++++++++++++++++++++++++++------------------------ audio/headset.h | 1 + audio/manager.c | 6 +- 3 files changed, 297 insertions(+), 222 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index c1d7ea24..a42b5131 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -109,6 +109,7 @@ struct headset { int rfcomm_ch; GIOChannel *rfcomm; + GIOChannel *tmp_rfcomm; GIOChannel *sco; guint sco_id; @@ -210,22 +211,273 @@ static int report_indicators(struct device *device, const char *buf) return headset_send(hs, "\r\nOK\r\n"); } -static int event_reporting(struct device *device, const char *buf) +static void pending_connect_complete(struct connect_cb *cb, struct device *dev) { - struct headset *hs = device->headset; - return headset_send(hs, "\r\nOK\r\n"); + struct headset *hs = dev->headset; + + if (hs->pending->err) + cb->cb(NULL, cb->cb_data); + else + cb->cb(dev, cb->cb_data); } -static int call_hold(struct device *device, const char *buf) +static void pending_connect_finalize(struct device *dev) { - struct headset *hs = device->headset; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; + + g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev); + + g_slist_foreach(p->callbacks, (GFunc) g_free, NULL); + g_slist_free(p->callbacks); + + if (p->io) { + g_io_channel_close(p->io); + g_io_channel_unref(p->io); + } + + if (p->msg) + dbus_message_unref(p->msg); + + if (p->call) { + dbus_pending_call_cancel(p->call); + dbus_pending_call_unref(p->call); + } + + g_free(p); + + hs->pending = NULL; +} + +static void pending_connect_init(struct headset *hs, headset_state_t target_state) +{ + if (hs->pending) { + if (hs->pending->target_state < target_state) + hs->pending->target_state = target_state; + return; + } + + hs->pending = g_new0(struct pending_connect, 1); + hs->pending->target_state = target_state; +} + +static unsigned int connect_cb_new(struct headset *hs, + headset_state_t target_state, + headset_stream_cb_t func, + void *user_data) +{ + struct connect_cb *cb; + unsigned int free_cb_id = 1; + + pending_connect_init(hs, target_state); + + cb = g_new(struct connect_cb, 1); + + cb->cb = func; + cb->cb_data = user_data; + cb->id = free_cb_id++; + + hs->pending->callbacks = g_slist_append(hs->pending->callbacks, + cb); + + return cb->id; +} + +static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, + struct device *device) +{ + struct headset *hs; + int ret, sk; + socklen_t len; + struct pending_connect *p; + + if (cond & G_IO_NVAL) + return FALSE; + + hs = device->headset; + p = hs->pending; + + sk = g_io_channel_unix_get_fd(chan); + + len = sizeof(ret); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + p->err = errno; + error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), + p->err); + goto failed; + } + + if (ret != 0) { + p->err = ret; + error("connect(): %s (%d)", strerror(ret), ret); + goto failed; + } + + debug("SCO socket opened for headset %s", device->path); + + info("SCO fd=%d", sk); + hs->sco = chan; + p->io = NULL; + + pending_connect_finalize(device); + + fcntl(sk, F_SETFL, 0); + + headset_set_state(device, HEADSET_STATE_PLAYING); + + return FALSE; + +failed: + pending_connect_finalize(device); + if (hs->rfcomm) + headset_set_state(device, HEADSET_STATE_CONNECTED); + else + headset_set_state(device, HEADSET_STATE_DISCONNECTED); + + return FALSE; +} + +static int sco_connect(struct device *dev, headset_stream_cb_t cb, + void *user_data, unsigned int *cb_id) +{ + struct headset *hs = dev->headset; + struct sockaddr_sco addr; + GIOChannel *io; + int sk, err; + + if (hs->state != HEADSET_STATE_CONNECTED) + return -EINVAL; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + return -err; + } + + io = g_io_channel_unix_new(sk); + if (!io) { + close(sk); + return -ENOMEM; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, BDADDR_ANY); + + if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + err = errno; + error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + goto failed; + } + + if (set_nonblocking(sk) < 0) { + err = errno; + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + bacpy(&addr.sco_bdaddr, &dev->dst); + + err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { + err = errno; + error("connect: %s (%d)", strerror(errno), errno); + goto failed; + } + + headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS); + + pending_connect_init(hs, HEADSET_STATE_PLAYING); + + if (cb) { + unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING, + cb, user_data); + if (cb_id) + *cb_id = id; + } + + g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + (GIOFunc) sco_connect_cb, dev); + + hs->pending->io = io; + + return 0; + +failed: + g_io_channel_close(io); + g_io_channel_unref(io); + return -err; +} + +static void hfp_slc_complete(struct device *dev) +{ + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; + + debug("HFP Service Level Connection established"); + + headset_set_state(dev, HEADSET_STATE_CONNECTED); + + if (p == NULL) + return; + + if (p->msg) { + DBusMessage *reply = dbus_message_new_method_return(p->msg); + send_message_and_unref(dev->conn, reply); + } + + if (p->target_state == HEADSET_STATE_CONNECTED) { + pending_connect_finalize(dev); + return; + } + + p->err = sco_connect(dev, NULL, NULL, NULL); + if (p->err < 0) + pending_connect_finalize(dev); +} + +static int event_reporting(struct device *dev, const char *buf) +{ + struct headset *hs = dev->headset; + int ret; + + ret = headset_send(hs, "\r\nOK\r\n"); + if (ret < 0) + return ret; + + if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS) + return 0; + + if (ag_features & AG_FEATURE_THREE_WAY_CALLING) + return 0; + + hfp_slc_complete(dev); + + return 0; +} + +static int call_hold(struct device *dev, const char *buf) +{ + struct headset *hs = dev->headset; int err; err = headset_send(hs, "\r\n+CHLD:(0,1,1x,2,2x,3,4)\r\n"); if (err < 0) return err; - return headset_send(hs, "\r\nOK\r\n"); + err = headset_send(hs, "\r\nOK\r\n"); + if (err < 0) + return err; + + if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS) + return 0; + + hfp_slc_complete(dev); + + return 0; } static int answer_call(struct device *device, const char *buf) @@ -475,207 +727,6 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static void pending_connect_complete(struct connect_cb *cb, struct device *dev) -{ - struct headset *hs = dev->headset; - - if (hs->pending->err) - cb->cb(NULL, cb->cb_data); - else - cb->cb(dev, cb->cb_data); -} - -static void pending_connect_finalize(struct device *dev) -{ - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - - g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev); - - g_slist_foreach(p->callbacks, (GFunc) g_free, NULL); - g_slist_free(p->callbacks); - - if (p->io) { - g_io_channel_close(p->io); - g_io_channel_unref(p->io); - } - - if (p->msg) - dbus_message_unref(p->msg); - - if (p->call) { - dbus_pending_call_cancel(p->call); - dbus_pending_call_unref(p->call); - } - - g_free(p); - - hs->pending = NULL; -} - -static void pending_connect_init(struct headset *hs, headset_state_t target_state) -{ - if (hs->pending) { - if (hs->pending->target_state < target_state) - hs->pending->target_state = target_state; - return; - } - - hs->pending = g_new0(struct pending_connect, 1); - hs->pending->target_state = target_state; -} - -static unsigned int connect_cb_new(struct headset *hs, - headset_state_t target_state, - headset_stream_cb_t func, - void *user_data) -{ - struct connect_cb *cb; - unsigned int free_cb_id = 1; - - pending_connect_init(hs, target_state); - - cb = g_new(struct connect_cb, 1); - - cb->cb = func; - cb->cb_data = user_data; - cb->id = free_cb_id++; - - hs->pending->callbacks = g_slist_append(hs->pending->callbacks, - cb); - - return cb->id; -} - -static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - struct device *device) -{ - struct headset *hs; - int ret, sk; - socklen_t len; - struct pending_connect *p; - - if (cond & G_IO_NVAL) - return FALSE; - - hs = device->headset; - p = hs->pending; - - sk = g_io_channel_unix_get_fd(chan); - - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - p->err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), - p->err); - goto failed; - } - - if (ret != 0) { - p->err = ret; - error("connect(): %s (%d)", strerror(ret), ret); - goto failed; - } - - debug("SCO socket opened for headset %s", device->path); - - info("SCO fd=%d", sk); - hs->sco = chan; - p->io = NULL; - - pending_connect_finalize(device); - - fcntl(sk, F_SETFL, 0); - - headset_set_state(device, HEADSET_STATE_PLAYING); - - return FALSE; - -failed: - pending_connect_finalize(device); - if (hs->rfcomm) - headset_set_state(device, HEADSET_STATE_CONNECTED); - else - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - - return FALSE; -} - -static int sco_connect(struct device *dev, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id) -{ - struct headset *hs = dev->headset; - struct sockaddr_sco addr; - GIOChannel *io; - int sk, err; - - if (hs->state != HEADSET_STATE_CONNECTED) - return -EINVAL; - - sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); - if (sk < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - return -err; - } - - io = g_io_channel_unix_new(sk); - if (!io) { - close(sk); - return -ENOMEM; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, BDADDR_ANY); - - if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - goto failed; - } - - if (set_nonblocking(sk) < 0) { - err = errno; - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &dev->dst); - - err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); - - if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect: %s (%d)", strerror(errno), errno); - goto failed; - } - - headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS); - - pending_connect_init(hs, HEADSET_STATE_PLAYING); - - if (cb) { - unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING, - cb, user_data); - if (cb_id) - *cb_id = id; - } - - g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, dev); - - hs->pending->io = io; - - return 0; - -failed: - g_io_channel_close(io); - g_io_channel_unref(io); - return -err; -} - static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct device *dev) { @@ -715,12 +766,16 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, else hs->hfp_active = FALSE; - headset_set_state(dev, HEADSET_STATE_CONNECTED); + g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, dev); debug("%s: Connected to %s", dev->path, hs_address); - g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, dev); + /* In HFP mode wait for Service Level Connection */ + if (hs->hfp_active) + return FALSE; + + headset_set_state(dev, HEADSET_STATE_CONNECTED); if (p->target_state == HEADSET_STATE_PLAYING) { p->err = sco_connect(dev, NULL, NULL, NULL); @@ -729,6 +784,11 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } + if (p->msg) { + DBusMessage *reply = dbus_message_new_method_return(p->msg); + send_message_and_unref(dev->conn, reply); + } + pending_connect_finalize(dev); return FALSE; @@ -1939,22 +1999,25 @@ int headset_connect_rfcomm(struct device *dev, int sock) { struct headset *hs = dev->headset; - hs->rfcomm = g_io_channel_unix_new(sock); + hs->tmp_rfcomm = g_io_channel_unix_new(sock); - return hs->rfcomm ? 0 : -EINVAL; + return hs->tmp_rfcomm ? 0 : -EINVAL; } int headset_close_rfcomm(struct device *dev) { struct headset *hs = dev->headset; + GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm; if (hs->ring_timer) { g_source_remove(hs->ring_timer); hs->ring_timer = 0; } - if (hs->rfcomm) { - g_io_channel_close(hs->rfcomm); - g_io_channel_unref(hs->rfcomm); + + if (rfcomm) { + g_io_channel_close(rfcomm); + g_io_channel_unref(rfcomm); + hs->tmp_rfcomm = NULL; hs->rfcomm = NULL; } @@ -1964,6 +2027,21 @@ int headset_close_rfcomm(struct device *dev) return 0; } +void headset_set_authorized(struct device *dev) +{ + struct headset *hs = dev->headset; + + hs->rfcomm = hs->tmp_rfcomm; + hs->tmp_rfcomm = NULL; + + g_io_add_watch(hs->rfcomm, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) rfcomm_io_cb, dev); + + if (!hs->hfp_active) + headset_set_state(dev, HEADSET_STATE_CONNECTED); +} + void headset_set_state(struct device *dev, headset_state_t state) { struct headset *hs = dev->headset; @@ -1985,10 +2063,6 @@ void headset_set_state(struct device *dev, headset_state_t state) case HEADSET_STATE_CONNECTED: close_sco(dev); if (hs->state < state) { - g_io_add_watch(hs->rfcomm, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, dev); - dbus_connection_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Connected", diff --git a/audio/headset.h b/audio/headset.h index 5a61bd56..b218b46e 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -58,6 +58,7 @@ gboolean headset_cancel_stream(struct device *dev, unsigned int id); gboolean get_hfp_active(struct device *dev); void set_hfp_active(struct device *dev, gboolean active); +void headset_set_authorized(struct device *dev); int headset_connect_rfcomm(struct device *dev, int sock); int headset_close_rfcomm(struct device *dev); diff --git a/audio/manager.c b/audio/manager.c index c45e45db..69f9237f 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1405,7 +1405,7 @@ static void auth_cb(DBusPendingCall *call, void *data) } else { char hs_address[18]; - headset_set_state(device, HEADSET_STATE_CONNECTED); + headset_set_authorized(device); ba2str(&device->dst, hs_address); @@ -1464,14 +1464,14 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } + set_hfp_active(device, hfp_active); + if (headset_connect_rfcomm(device, cli_sk) < 0) { error("Allocating new GIOChannel failed!"); close(cli_sk); return TRUE; } - set_hfp_active(device, hfp_active); - if (!manager_authorize(&device->dst, uuid, auth_cb, device, NULL)) goto failed; -- cgit From ff7353d5c40698a036bcc1e87cd26f2bb3ff3b87 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 19 Feb 2008 09:20:42 +0000 Subject: Implement basic headset automatic disconnect support --- audio/headset.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index a42b5131..f507bfb9 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -113,6 +113,8 @@ struct headset { GIOChannel *sco; guint sco_id; + gboolean auto_dc; + guint ring_timer; char buf[BUF_SIZE]; @@ -1281,6 +1283,8 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, if (err < 0) return error_connection_attempt_failed(conn, msg, -err); + hs->auto_dc = FALSE; + hs->pending->msg = dbus_message_ref(msg); return DBUS_HANDLER_RESULT_HANDLED; @@ -1937,10 +1941,8 @@ gboolean headset_cancel_stream(struct device *dev, unsigned int id) pending_connect_finalize(dev); - /* FIXME: disconnecting is not correct for all scenarios, e.g. if the - * stream request came when we were already connected then we should - * stay connected after canceling the request too */ - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + if (hs->auto_dc) + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return TRUE; } @@ -1956,7 +1958,6 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, { struct headset *hs = dev->headset; unsigned int id; - int err; if (hs->rfcomm && hs->sco) { id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); @@ -1964,21 +1965,21 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, return id; } - if (hs->rfcomm == NULL) - err = rfcomm_connect(dev, cb, user_data, &id); - else if (hs->sco == NULL) - err = sco_connect(dev, cb, user_data, &id); + if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS) + return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); - if (err < 0) - goto error; + if (hs->rfcomm == NULL) { + if (rfcomm_connect(dev, cb, user_data, &id) < 0) + return 0; + hs->auto_dc = TRUE; + } else { + if (sco_connect(dev, cb, user_data, &id) < 0) + return 0; + } hs->pending->target_state = HEADSET_STATE_PLAYING; return id; - -error: - pending_connect_finalize(dev); - return 0; } gboolean get_hfp_active(struct device *dev) @@ -2038,6 +2039,8 @@ void headset_set_authorized(struct device *dev) G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) rfcomm_io_cb, dev); + hs->auto_dc = FALSE; + if (!hs->hfp_active) headset_set_state(dev, HEADSET_STATE_CONNECTED); } @@ -2142,8 +2145,13 @@ gboolean headset_unlock(struct device *dev, headset_lock_t lock) hs->lock &= ~lock; - if (!hs->lock && hs->state > HEADSET_STATE_DISCONNECTED) + if (hs->lock) + return TRUE; + + if (hs->auto_dc) headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + else if (hs->state == HEADSET_STATE_PLAYING) + headset_set_state(dev, HEADSET_STATE_CONNECTED); return TRUE; } -- cgit From a8bdd2f6be0cdb932110290f3534a525d99c5031 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 19 Feb 2008 15:53:20 +0000 Subject: Refuse Headset.Play() when HCI routing is configured --- audio/headset.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index f507bfb9..faaf47f5 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1403,6 +1403,12 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, struct headset *hs = device->headset; int err; + if (sco_hci) { + error("Refusing Headset.Play() because SCO HCI routing " + "is enabled"); + return error_not_available(conn, msg); + } + switch (hs->state) { case HEADSET_STATE_DISCONNECTED: case HEADSET_STATE_CONNECT_IN_PROGRESS: -- cgit From 23a5ed9f697a1e42a0d36c72dd9a297792c61082 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 19 Feb 2008 19:48:23 +0000 Subject: Fix alsa plugin to make use of sbc new API. --- audio/pcm_bluetooth.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index ef12b400..2282b2ed 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -610,53 +610,54 @@ static void bluetooth_a2dp_setup(struct bluetooth_a2dp *a2dp) a2dp->sbc_initialized = 1; if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000) - a2dp->sbc.rate = 16000; + a2dp->sbc.frequency = SBC_FREQ_16000; if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000) - a2dp->sbc.rate = 32000; + a2dp->sbc.frequency = SBC_FREQ_32000; if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100) - a2dp->sbc.rate = 44100; + a2dp->sbc.frequency = SBC_FREQ_44100; if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000) - a2dp->sbc.rate = 48000; + a2dp->sbc.frequency = SBC_FREQ_48000; if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - a2dp->sbc.channels = 1; - else - a2dp->sbc.channels = 2; + a2dp->sbc.mode = SBC_MODE_MONO; - if (active_capabilities.channel_mode & - (BT_A2DP_CHANNEL_MODE_MONO || - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - a2dp->sbc.joint = 1; - else - a2dp->sbc.joint = 0; + if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) + a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL; + + if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) + a2dp->sbc.mode = SBC_MODE_STEREO; + + if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) + a2dp->sbc.mode = SBC_MODE_JOINT_STEREO; a2dp->sbc.allocation = active_capabilities.allocation_method - == BT_A2DP_ALLOCATION_SNR ? 0x01 : 0x00; + == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR + : SBC_AM_LOUDNESS; switch (active_capabilities.subbands) { case BT_A2DP_SUBBANDS_4: - a2dp->sbc.subbands = 4; + a2dp->sbc.subbands = SBC_SB_4; break; case BT_A2DP_SUBBANDS_8: - a2dp->sbc.subbands = 8; + a2dp->sbc.subbands = SBC_SB_8; break; } switch (active_capabilities.block_length) { case BT_A2DP_BLOCK_LENGTH_4: - a2dp->sbc.blocks = 4; + a2dp->sbc.blocks = SBC_BLK_4; break; case BT_A2DP_BLOCK_LENGTH_8: - a2dp->sbc.blocks = 8; + a2dp->sbc.blocks = SBC_BLK_8; break; case BT_A2DP_BLOCK_LENGTH_12: - a2dp->sbc.blocks = 12; + a2dp->sbc.blocks = SBC_BLK_12; break; case BT_A2DP_BLOCK_LENGTH_16: - a2dp->sbc.blocks = 16; + a2dp->sbc.blocks = SBC_BLK_16; break; } -- cgit From a8b3b548ec9870df10709a839ed4c07e6d8eb76d Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 19 Feb 2008 19:49:24 +0000 Subject: Update gstreamer plugin to use new sbc API. --- audio/gsta2dpsink.c | 1 - audio/gstavdtpsink.c | 1 - audio/gstbluetooth.c | 2 + audio/gstrtpsbcpay.c | 3 +- audio/gstsbcdec.c | 8 +- audio/gstsbcdec.h | 1 + audio/gstsbcenc.c | 57 +++++------ audio/gstsbcutil.c | 283 +++++++++++++++++++++++++-------------------------- audio/gstsbcutil.h | 35 ++++--- 9 files changed, 199 insertions(+), 192 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 9ce73345..0b61e826 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -28,7 +28,6 @@ #include #include -#include "gstsbcutil.h" #include "gsta2dpsink.h" GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug); diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c index 8c8a7465..4c453ae9 100644 --- a/audio/gstavdtpsink.c +++ b/audio/gstavdtpsink.c @@ -39,7 +39,6 @@ #include "ipc.h" #include "rtp.h" -#include "gstsbcutil.h" #include "gstavdtpsink.h" diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c index b6b95e43..ffc69768 100644 --- a/audio/gstbluetooth.c +++ b/audio/gstbluetooth.c @@ -27,6 +27,8 @@ #include +#include + #include "gstsbcutil.h" #include diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 235ad6bb..70f5bef2 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -217,9 +217,10 @@ static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, /* FIXME check for negotiation */ sbcpay = GST_RTP_SBC_PAY(payload); + sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer); + gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); - sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer); available = gst_adapter_available(sbcpay->adapter); if (available + RTP_SBC_HEADER_TOTAL >= GST_BASE_RTP_PAYLOAD_MTU(sbcpay) || diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index d6777117..43a40f44 100644 --- a/audio/gstsbcdec.c +++ b/audio/gstsbcdec.c @@ -99,8 +99,12 @@ static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) /* we will reuse the same caps object */ if (dec->outcaps == NULL) { caps = gst_caps_new_simple("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sbc.rate, - "channels", G_TYPE_INT, dec->sbc.channels, + "rate", G_TYPE_INT, + gst_sbc_parse_rate_from_sbc( + dec->sbc.frequency), + "channels", G_TYPE_INT, + gst_sbc_get_channel_number( + dec->sbc.mode), NULL); template = gst_static_pad_template_get(&sbc_dec_src_factory); diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index e8ff9cf5..df687933 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -24,6 +24,7 @@ #include #include "sbc.h" +#include "gstsbcutil.h" G_BEGIN_DECLS diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 7777084c..03423ecf 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -27,14 +27,13 @@ #include -#include "ipc.h" #include "gstsbcenc.h" #include "gstsbcutil.h" -#define SBC_ENC_DEFAULT_MODE BT_A2DP_CHANNEL_MODE_JOINT_STEREO +#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO #define SBC_ENC_DEFAULT_BLOCKS 0 #define SBC_ENC_DEFAULT_SUB_BANDS 0 -#define SBC_ENC_DEFAULT_ALLOCATION BT_A2DP_ALLOCATION_LOUDNESS +#define SBC_ENC_DEFAULT_ALLOCATION SBC_AM_AUTO #define SBC_ENC_DEFAULT_RATE 0 #define SBC_ENC_DEFAULT_CHANNELS 0 @@ -53,11 +52,11 @@ static GType gst_sbc_mode_get_type(void) { static GType sbc_mode_type = 0; static GEnumValue sbc_modes[] = { - { 0, "Auto", "auto" }, - { 1, "Mono", "mono" }, - { 2, "Dual Channel", "dual" }, - { 3, "Stereo", "stereo" }, - { 4, "Joint Stereo", "joint" }, + { SBC_MODE_MONO, "Mono", "mono" }, + { SBC_MODE_DUAL_CHANNEL, "Dual Channel", "dual" }, + { SBC_MODE_STEREO, "Stereo", "stereo"}, + { SBC_MODE_JOINT_STEREO, "Joint Stereo", "joint" }, + { SBC_MODE_AUTO, "Auto", "auto" }, { -1, NULL, NULL} }; @@ -73,8 +72,9 @@ static GType gst_sbc_allocation_get_type(void) { static GType sbc_allocation_type = 0; static GEnumValue sbc_allocations[] = { - { BT_A2DP_ALLOCATION_LOUDNESS, "Loudness", "loudness" }, - { BT_A2DP_ALLOCATION_SNR, "SNR", "snr" }, + { SBC_AM_LOUDNESS, "Loudness", "loudness" }, + { SBC_AM_SNR, "SNR", "snr" }, + { SBC_AM_AUTO, "Auto", "auto" }, { -1, NULL, NULL} }; @@ -172,7 +172,6 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) GstStructure *structure; GEnumValue *enum_value; GEnumClass *enum_class; - gchar* temp; GValue *value; src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); @@ -208,7 +207,7 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) g_type_class_unref(enum_class); } - if (enc->allocation != SBC_ENC_DEFAULT_ALLOCATION) { + if (enc->allocation != SBC_AM_AUTO) { enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); enum_value = g_enum_get_value(enum_class, enc->allocation); gst_sbc_util_set_structure_string_param(structure, "allocation", @@ -216,9 +215,6 @@ static GstCaps* sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) g_type_class_unref(enum_class); } - temp = gst_caps_to_string(src_caps); - GST_DEBUG_OBJECT(enc, "Srcpad caps: %s", temp); - g_free(temp); g_free(value); return src_caps; @@ -244,7 +240,6 @@ static gboolean sbc_enc_src_setcaps (GstPad *pad, GstCaps *caps) static GstCaps* sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) { - gchar *error_message = NULL; GstCaps* result; @@ -316,8 +311,6 @@ static gboolean sbc_enc_sink_setcaps (GstPad * pad, GstCaps * caps) gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) { - gint mode; - if (!gst_caps_is_fixed(caps)) { GST_DEBUG_OBJECT(enc, "didn't receive fixed caps, " "returning false"); @@ -327,23 +320,26 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps)) return FALSE; - if (enc->rate != 0 && enc->sbc.rate != enc->rate) + if (enc->rate != 0 && gst_sbc_parse_rate_from_sbc(enc->sbc.frequency) + != enc->rate) goto fail; - if (enc->channels != 0 && enc->sbc.channels != enc->channels) + if (enc->channels != 0 && gst_sbc_get_channel_number(enc->sbc.mode) + != enc->channels) goto fail; - if (enc->blocks != 0 && enc->sbc.blocks != enc->blocks) + if (enc->blocks != 0 && gst_sbc_parse_blocks_from_sbc(enc->sbc.blocks) + != enc->blocks) goto fail; - if (enc->subbands != 0 && enc->sbc.subbands != enc->subbands) + if (enc->subbands != 0 && gst_sbc_parse_subbands_from_sbc( + enc->sbc.subbands) != enc->subbands) goto fail; - mode = gst_sbc_get_mode_int_from_sbc_t(&enc->sbc); - if (enc->mode != SBC_ENC_DEFAULT_MODE && mode != enc->mode) + if (enc->mode != SBC_ENC_DEFAULT_MODE && enc->sbc.mode != enc->mode) goto fail; - if (enc->allocation != SBC_ENC_DEFAULT_ALLOCATION && + if (enc->allocation != SBC_AM_AUTO && enc->sbc.allocation != enc->allocation) goto fail; @@ -355,8 +351,9 @@ gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) enc->frame_length = sbc_get_frame_length(&enc->sbc); enc->frame_duration = sbc_get_frame_duration(&enc->sbc); - GST_DEBUG("codesize: %d, frame_length: %d, frame_duration: %d", - enc->codesize, enc->frame_length, enc->frame_duration); + GST_DEBUG_OBJECT(enc, "codesize: %d, frame_length: %d, frame_duration:" + " %d", enc->codesize, enc->frame_length, + enc->frame_duration); return TRUE; @@ -378,7 +375,7 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GstBuffer *output; GstCaps *caps; const guint8 *data; - int consumed; + gint consumed; caps = GST_PAD_CAPS(enc->srcpad); res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, @@ -389,12 +386,13 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) goto done; data = gst_adapter_peek(adapter, enc->codesize); + consumed = sbc_encode(&enc->sbc, (gpointer) data, enc->codesize, GST_BUFFER_DATA(output), GST_BUFFER_SIZE(output), NULL); if (consumed <= 0) { - GST_ERROR ("comsumed < 0, codesize: %d", + GST_DEBUG_OBJECT (enc, "comsumed < 0, codesize: %d", enc->codesize); break; } @@ -405,6 +403,7 @@ static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) GST_BUFFER_DURATION(output) = enc->frame_duration; res = gst_pad_push(enc->srcpad, output); + if (res != GST_FLOW_OK) goto done; } diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c index de48838f..5d646cfb 100644 --- a/audio/gstsbcutil.c +++ b/audio/gstsbcutil.c @@ -25,7 +25,6 @@ #include #endif -#include "ipc.h" #include #include "gstsbcutil.h" @@ -120,163 +119,192 @@ const gchar *gst_sbc_get_mode_from_list(const GValue *list, gint channels) return "joint"; else if (stereo) return "stereo"; + else if (dual) + return "dual"; } return NULL; } -gint gst_sbc_get_allocation_mode_int(const gchar *allocation) +gint gst_sbc_parse_rate_from_sbc(gint frequency) { - if (g_ascii_strcasecmp(allocation, "loudness") == 0) - return BT_A2DP_ALLOCATION_LOUDNESS; - else if (g_ascii_strcasecmp(allocation, "snr") == 0) - return BT_A2DP_ALLOCATION_SNR; - else - return -1; + switch (frequency) { + case SBC_FREQ_16000: + return 16000; + case SBC_FREQ_32000: + return 32000; + case SBC_FREQ_44100: + return 44100; + case SBC_FREQ_48000: + return 48000; + default: + return 0; + } } -gint gst_sbc_get_mode_int(const gchar *mode) +gint gst_sbc_parse_rate_to_sbc(gint rate) { - if (g_ascii_strcasecmp(mode, "joint") == 0) - return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (g_ascii_strcasecmp(mode, "stereo") == 0) - return BT_A2DP_CHANNEL_MODE_STEREO; - else if (g_ascii_strcasecmp(mode, "dual") == 0) - return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (g_ascii_strcasecmp(mode, "mono") == 0) - return BT_A2DP_CHANNEL_MODE_MONO; - else + switch (rate) { + case 16000: + return SBC_FREQ_16000; + case 32000: + return SBC_FREQ_32000; + case 44100: + return SBC_FREQ_44100; + case 48000: + return SBC_FREQ_48000; + default: return -1; + } } -/* FIXME add dual when sbc_t supports it */ -gboolean gst_sbc_get_mode_int_for_sbc_t(const gchar *mode) +gint gst_sbc_get_channel_number(gint mode) { - if (g_ascii_strcasecmp(mode, "joint") == 0) - return TRUE; - else if (g_ascii_strcasecmp(mode, "stereo") == 0) - return FALSE; - else if (g_ascii_strcasecmp(mode, "mono") == 0) - return FALSE; - else - return -1; + switch (mode) { + case SBC_MODE_JOINT_STEREO: + case SBC_MODE_STEREO: + case SBC_MODE_DUAL_CHANNEL: + return 2; + case SBC_MODE_MONO: + return 1; + default: + return 0; + } } -gint gst_sbc_get_mode_int_from_sbc_t(const sbc_t *sbc) +gint gst_sbc_parse_subbands_from_sbc(gint subbands) { - /* TODO define constants */ - if (sbc->channels == 2 && sbc->joint == 1) + switch (subbands) { + case SBC_SB_4: return 4; - else if (sbc->channels == 2 && sbc->joint == 0) - return 3; - else if (sbc->channels == 1) - return 1; - else - return -1; + case SBC_SB_8: + return 8; + default: + return 0; + } } -const gchar *gst_sbc_get_mode_string(gint joint) +gint gst_sbc_parse_subbands_to_sbc(gint subbands) { - switch (joint) { - case BT_A2DP_CHANNEL_MODE_MONO: - return "mono"; - case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return "dual"; - case BT_A2DP_CHANNEL_MODE_STEREO: - return "stereo"; - case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: - return "joint"; + switch (subbands) { + case 4: + return SBC_SB_4; + case 8: + return SBC_SB_8; default: - return NULL; + return -1; } } -const gchar *gst_sbc_get_allocation_string(gint alloc) +gint gst_sbc_parse_blocks_from_sbc(gint blocks) { - switch (alloc) { - case BT_A2DP_ALLOCATION_LOUDNESS: - return "loudness"; - case BT_A2DP_ALLOCATION_SNR: - return "snr"; + switch (blocks) { + case SBC_BLK_4: + return 4; + case SBC_BLK_8: + return 8; + case SBC_BLK_12: + return 12; + case SBC_BLK_16: + return 16; default: - return NULL; + return 0; } } -/* channel mode */ -#define SBC_CM_MONO 0x00 -#define SBC_CM_DUAL_CHANNEL 0x01 -#define SBC_CM_STEREO 0x02 -#define SBC_CM_JOINT_STEREO 0x03 - -/* allocation mode */ -#define SBC_AM_LOUDNESS 0x00 -#define SBC_AM_SNR 0x01 +gint gst_sbc_parse_blocks_to_sbc(gint blocks) +{ + switch (blocks) { + case 4: + return SBC_BLK_4; + case 8: + return SBC_BLK_8; + case 12: + return SBC_BLK_12; + case 16: + return SBC_BLK_16; + default: + return -1; + } +} -const gchar *gst_sbc_get_mode_string_from_sbc_t(gint channels, gint joint) +const gchar *gst_sbc_parse_mode_from_sbc(gint mode) { - if (channels == 2 && joint == 1) - return "joint"; - else if (channels == 2 && joint == 0) - return "stereo"; - else if (channels == 1 && joint == 0) + switch (mode) { + case SBC_MODE_MONO: return "mono"; - else + case SBC_MODE_DUAL_CHANNEL: + return "dual"; + case SBC_MODE_STEREO: + return "stereo"; + case SBC_MODE_JOINT_STEREO: + case SBC_MODE_AUTO: + return "joint"; + default: return NULL; + } } -const gchar *gst_sbc_get_allocation_string_from_sbc_t(gint alloc) +gint gst_sbc_parse_mode_to_sbc(const gchar *mode) +{ + if (g_ascii_strcasecmp(mode, "joint") == 0) + return SBC_MODE_JOINT_STEREO; + else if (g_ascii_strcasecmp(mode, "stereo") == 0) + return SBC_MODE_STEREO; + else if (g_ascii_strcasecmp(mode, "dual") == 0) + return SBC_MODE_DUAL_CHANNEL; + else if (g_ascii_strcasecmp(mode, "mono") == 0) + return SBC_MODE_MONO; + else if (g_ascii_strcasecmp(mode, "auto") == 0) + return SBC_MODE_JOINT_STEREO; + else + return -1; +} + +const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc) { switch (alloc) { case SBC_AM_LOUDNESS: return "loudness"; case SBC_AM_SNR: return "snr"; + case SBC_AM_AUTO: + return "loudness"; default: return NULL; } } -GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc) +gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation) { - GstCaps *caps; - const gchar *mode_str; - const gchar *allocation_str; - - mode_str = gst_sbc_get_mode_string_from_sbc_t(sbc->channels, - sbc->joint); - allocation_str = gst_sbc_get_allocation_string_from_sbc_t( - sbc->allocation); - caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, sbc->rate, - "channels", G_TYPE_INT, sbc->channels, - "mode", G_TYPE_STRING, mode_str, - "subbands", G_TYPE_INT, sbc->subbands, - "blocks", G_TYPE_INT, sbc->blocks, - "allocation", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->bitpool, - NULL); - - return caps; + if (g_ascii_strcasecmp(allocation, "loudness") == 0) + return SBC_AM_LOUDNESS; + else if (g_ascii_strcasecmp(allocation, "snr") == 0) + return SBC_AM_SNR; + else + return SBC_AM_LOUDNESS; } -GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels) +GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc) { GstCaps *caps; const gchar *mode_str; const gchar *allocation_str; - mode_str = gst_sbc_get_mode_string(sbc->channel_mode); - allocation_str = gst_sbc_get_allocation_string(sbc->allocation_method); - + mode_str = gst_sbc_parse_mode_from_sbc(sbc->mode); + allocation_str = gst_sbc_parse_allocation_from_sbc(sbc->allocation); caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, sbc->frequency, - "channels", G_TYPE_INT, channels, + "rate", G_TYPE_INT, + gst_sbc_parse_rate_from_sbc(sbc->frequency), + "channels", G_TYPE_INT, + gst_sbc_get_channel_number(sbc->mode), "mode", G_TYPE_STRING, mode_str, - "subbands", G_TYPE_INT, sbc->subbands, - "blocks", G_TYPE_INT, sbc->block_length, + "subbands", G_TYPE_INT, + gst_sbc_parse_subbands_from_sbc(sbc->subbands), + "blocks", G_TYPE_INT, + gst_sbc_parse_blocks_from_sbc(sbc->blocks), "allocation", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->max_bitpool, + "bitpool", G_TYPE_INT, sbc->bitpool, NULL); return caps; @@ -393,12 +421,11 @@ GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message) mode = g_value_get_string(value); } - /* perform validation + /* perform validation * if channels is 1, we must have channel mode = mono - * if channels is 2, we can't have channel mode = mono, dual */ + * if channels is 2, we can't have channel mode = mono */ if ( (channels == 1 && (strcmp(mode, "mono") != 0) ) || - ( channels == 2 && ( strcmp(mode, "mono") == 0 || - strcmp(mode, "dual") == 0 ) )) { + ( channels == 2 && ( strcmp(mode, "mono") == 0))) { *error_message = g_strdup_printf("Invalid combination of " "channels (%d) and channel mode (%s)", channels, mode); @@ -479,46 +506,16 @@ gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps) if (!(allocation = gst_structure_get_string(structure, "allocation"))) return FALSE; - sbc->rate = rate; - sbc->channels = channels; - sbc->blocks = blocks; - sbc->subbands = subbands; + if (channels == 1 && strcmp(mode, "mono") != 0) + return FALSE; + + sbc->frequency = gst_sbc_parse_rate_to_sbc(rate); + sbc->blocks = gst_sbc_parse_blocks_to_sbc(blocks); + sbc->subbands = gst_sbc_parse_subbands_to_sbc(subbands); sbc->bitpool = bitpool; - sbc->joint = gst_sbc_get_mode_int_for_sbc_t(mode); - sbc->allocation = gst_sbc_get_allocation_mode_int(allocation); + sbc->mode = gst_sbc_parse_mode_to_sbc(mode); + sbc->allocation = gst_sbc_parse_allocation_to_sbc(allocation); return TRUE; } -gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, - gint blocks, gint bitpool, gint channel_mode) -{ - gint len; - gint join; - len = 4 + (4 * subbands * channels)/8; - - if (channel_mode == BT_A2DP_CHANNEL_MODE_MONO || - channel_mode == BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) - len += ((blocks * channels * bitpool)+7) / 8; - else { - join = channel_mode == BT_A2DP_CHANNEL_MODE_JOINT_STEREO - ? 1 : 0; - len += ((join * subbands + blocks * bitpool)+7)/8; - } - - return len; -} - -gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, - gint blocks) -{ - return (((frame_len * 8 * rate / subbands) / blocks) / 1000); -} - -gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands) -{ - gint64 res = 1000000; - return res * blocks * subbands / rate; -} - - diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h index c0e876fd..a3cf937c 100644 --- a/audio/gstsbcutil.h +++ b/audio/gstsbcutil.h @@ -24,7 +24,10 @@ #include #include "sbc.h" -#include "ipc.h" +#include + +#define SBC_AM_AUTO 0x02 +#define SBC_MODE_AUTO 0x04 gint gst_sbc_select_rate_from_list(const GValue *value); @@ -39,15 +42,25 @@ gint gst_sbc_select_bitpool_from_range(const GValue *value); gint gst_sbc_select_bitpool_from_range(const GValue *value); const gchar *gst_sbc_get_allocation_from_list(const GValue *value); -gint gst_sbc_get_allocation_mode_int(const gchar *allocation); -const gchar *gst_sbc_get_allocation_string(int alloc); const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels); -gint gst_sbc_get_mode_int(const gchar *mode); -gint gst_sbc_get_mode_int_from_sbc_t(const sbc_t *sbc); -const gchar *gst_sbc_get_mode_string(int joint); -GstCaps* gst_sbc_caps_from_sbc(sbc_capabilities_t *sbc, gint channels); +gint gst_sbc_get_channel_number(gint mode); +gint gst_sbc_parse_rate_from_sbc(gint frequency); +gint gst_sbc_parse_rate_to_sbc(gint rate); + +gint gst_sbc_parse_subbands_from_sbc(gint subbands); +gint gst_sbc_parse_subbands_to_sbc(gint subbands); + +gint gst_sbc_parse_blocks_from_sbc(gint blocks); +gint gst_sbc_parse_blocks_to_sbc(gint blocks); + +const gchar *gst_sbc_parse_mode_from_sbc(gint mode); +gint gst_sbc_parse_mode_to_sbc(const gchar *mode); + +const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc); +gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation); + GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc); GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); @@ -62,11 +75,3 @@ void gst_sbc_util_set_structure_string_param(GstStructure *structure, gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps); -gint gst_sbc_util_calc_frame_len(gint subbands, gint channels, - gint blocks, gint bitpool, gint channel_mode); - -gint gst_sbc_util_calc_bitrate(gint frame_len, gint rate, gint subbands, - gint blocks); - -gint64 gst_sbc_util_calc_frame_duration(gint rate, gint blocks, gint subbands); - -- cgit From 41387ca1ae954c4b8ea99e8e4e1646198af254a7 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 20 Feb 2008 13:37:00 +0000 Subject: Fix runtime warnings of gstreamer plugin. --- audio/gsta2dpsink.c | 6 +++--- audio/gstrtpsbcpay.c | 6 +++--- audio/gstsbcenc.c | 4 ++-- audio/gstsbcparse.c | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'audio') diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c index 0b61e826..d25cbded 100644 --- a/audio/gsta2dpsink.c +++ b/audio/gsta2dpsink.c @@ -57,13 +57,13 @@ static GstStaticPadTemplate gst_a2dp_sink_factory = GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "mode = (string) { mono, dual, stereo, joint }, " + "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }, " + "allocation = (string) { \"snr\", \"loudness\" }, " "bitpool = (int) [ 2, " TEMPLATE_MAX_BITPOOL_STR " ]; " - "audio/mpeg;" + "audio/mpeg" )); static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event); diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 70f5bef2..39cf1970 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -80,11 +80,11 @@ static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "mode = (string) { mono, dual, stereo, joint }, " + "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }," - "bitpool = (int) [ 2, 64 ]; ") + "allocation = (string) { \"snr\", \"loudness\" }, " + "bitpool = (int) [ 2, 64 ]") ); static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c index 03423ecf..65e3da5b 100644 --- a/audio/gstsbcenc.c +++ b/audio/gstsbcenc.c @@ -157,10 +157,10 @@ static GstStaticPadTemplate sbc_enc_src_factory = GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "mode = (string) { mono, dual, stereo, joint }, " + "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }," + "allocation = (string) { \"snr\", \"loudness\" }, " "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR ", " SBC_ENC_BITPOOL_MAX_STR " ]")); diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c index 7b30dd2d..4521c563 100644 --- a/audio/gstsbcparse.c +++ b/audio/gstsbcparse.c @@ -51,10 +51,10 @@ static GstStaticPadTemplate sbc_parse_src_factory = GST_STATIC_CAPS("audio/x-sbc, " "rate = (int) { 16000, 32000, 44100, 48000 }, " "channels = (int) [ 1, 2 ], " - "mode = (string) { mono, dual, stereo, joint }, " + "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " "blocks = (int) { 4, 8, 12, 16 }, " "subbands = (int) { 4, 8 }, " - "allocation = (string) { snr, loudness }," + "allocation = (string) { \"snr\", \"loudness\" }," "bitpool = (int) [ 2, 64 ]," "parsed = (boolean) true")); -- cgit From 68c897fe0f096be9898ffa8f7ec0150f1cb9f961 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 28 Feb 2008 11:14:20 +0000 Subject: Add 3 second timer before disconnecting unused HSP/HSP connections --- audio/headset.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index faaf47f5..83752cdf 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -58,6 +58,8 @@ #include "error.h" #include "headset.h" +#define DC_TIMEOUT 3000 + #define RING_INTERVAL 3000 #define BUF_SIZE 1024 @@ -117,6 +119,8 @@ struct headset { guint ring_timer; + guint dc_timer; + char buf[BUF_SIZE]; int data_start; int data_length; @@ -1903,6 +1907,11 @@ void headset_free(struct device *dev) { struct headset *hs = dev->headset; + if (hs->dc_timer) { + g_source_remove(hs->dc_timer); + hs->dc_timer = 0; + } + if (hs->sco) { g_io_channel_close(hs->sco); g_io_channel_unref(hs->sco); @@ -1917,6 +1926,12 @@ void headset_free(struct device *dev) dev->headset = NULL; } +static gboolean hs_dc_timeout(struct device *dev) +{ + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + return FALSE; +} + gboolean headset_cancel_stream(struct device *dev, unsigned int id) { struct headset *hs = dev->headset; @@ -1947,8 +1962,14 @@ gboolean headset_cancel_stream(struct device *dev, unsigned int id) pending_connect_finalize(dev); - if (hs->auto_dc) - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + if (hs->auto_dc) { + if (hs->rfcomm) + hs->dc_timer = g_timeout_add(DC_TIMEOUT, + (GSourceFunc) hs_dc_timeout, + dev); + else + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + } return TRUE; } @@ -1971,6 +1992,11 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, return id; } + if (hs->dc_timer) { + g_source_remove(hs->dc_timer); + hs->dc_timer = 0; + } + if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS) return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); @@ -2154,11 +2180,18 @@ gboolean headset_unlock(struct device *dev, headset_lock_t lock) if (hs->lock) return TRUE; - if (hs->auto_dc) - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - else if (hs->state == HEADSET_STATE_PLAYING) + if (hs->state == HEADSET_STATE_PLAYING) headset_set_state(dev, HEADSET_STATE_CONNECTED); + if (hs->auto_dc) { + if (hs->state == HEADSET_STATE_CONNECTED) + hs->dc_timer = g_timeout_add(DC_TIMEOUT, + (GSourceFunc) hs_dc_timeout, + dev); + else + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); + } + return TRUE; } -- cgit From 7ae45c5a353a3e6a32044263cb1a367f237e6505 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 28 Feb 2008 19:38:53 +0000 Subject: Fix bug sending empty packages and remove a buffer copy. --- audio/gstrtpsbcpay.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 39cf1970..33640c43 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -185,6 +185,8 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) max_payload = MIN(max_payload, available); frame_count = max_payload / sbcpay->frame_length; payload_length = frame_count * sbcpay->frame_length; + if (payload_length == 0) /* Nothing to send */ + return GST_FLOW_OK; outbuf = gst_rtp_buffer_new_allocate(payload_length + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); @@ -219,7 +221,7 @@ static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, sbcpay = GST_RTP_SBC_PAY(payload); sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer); - gst_adapter_push(sbcpay->adapter, gst_buffer_copy(buffer)); + gst_adapter_push(sbcpay->adapter, buffer); available = gst_adapter_available(sbcpay->adapter); if (available + RTP_SBC_HEADER_TOTAL >= -- cgit From 93f2cfda49d36c29a089012fd7e9df0bcaa8682e Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 29 Feb 2008 19:37:15 +0000 Subject: Remove possible extra memcpy for gstreamer plugin. --- audio/gstrtpsbcpay.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index 33640c43..d0fec58e 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -166,7 +166,6 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) guint max_payload; GstBuffer* outbuf; guint8 *payload_data; - guint8 *data; guint frame_count; guint payload_length; struct rtp_payload *payload; @@ -199,10 +198,9 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) memset(payload, 0, sizeof(struct rtp_payload)); payload->frame_count = frame_count; - data = gst_adapter_take(sbcpay->adapter, payload_length); - memcpy(payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, data, - payload_length); - g_free(data); + gst_adapter_copy(sbcpay->adapter, payload_data + + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length); + gst_adapter_flush(sbcpay->adapter, payload_length); GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", payload_length); -- cgit From 744fbe08ac5950a96cfbe193fcc2f664c26acf61 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 29 Feb 2008 19:50:10 +0000 Subject: Fix code style. --- audio/gstrtpsbcpay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c index d0fec58e..3627ad0b 100644 --- a/audio/gstrtpsbcpay.c +++ b/audio/gstrtpsbcpay.c @@ -198,9 +198,9 @@ static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) memset(payload, 0, sizeof(struct rtp_payload)); payload->frame_count = frame_count; - gst_adapter_copy(sbcpay->adapter, payload_data + + gst_adapter_copy(sbcpay->adapter, payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length); - gst_adapter_flush(sbcpay->adapter, payload_length); + gst_adapter_flush(sbcpay->adapter, payload_length); GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", payload_length); -- cgit From 211ec74f0e8ce372dd4958ddc2c7f8747fb65355 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 4 Mar 2008 13:24:38 +0000 Subject: Use "hfp" instead of "no_hfp" and "EnableHFP" instead of "DisableHFP" to make the options more clear to understand (avoid double negatives) --- audio/audio.conf | 4 ++-- audio/manager.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/audio.conf b/audio/audio.conf index 0d733105..07ed3ea4 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -16,9 +16,9 @@ # service interacts with remote headset devices) [Headset] -# Set to true to only support HSP +# Set to true to support HFP (in addition to HSP only which is the default) # Defaults to false -DisableHFP=true +EnableHFP=false # HFP Gateway features # Defaults to false diff --git a/audio/manager.c b/audio/manager.c index 69f9237f..c5f7df30 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1540,7 +1540,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; sdp_buf_t buf; - gboolean no_hfp = FALSE; + gboolean hfp = TRUE; GError *err = NULL; uint32_t features; @@ -1569,7 +1569,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) (GIOFunc) ag_io_cb, NULL); if (config) { - no_hfp = g_key_file_get_boolean(config, "Headset", "DisableHFP", + hfp = g_key_file_get_boolean(config, "Headset", "EnableHFP", &err); if (err) { debug("audio.conf: %s", err->message); @@ -1578,7 +1578,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) } } - if (no_hfp) + if (!hfp) return 0; chan = DEFAULT_HF_AG_CHANNEL; -- cgit From 37c2fa54a9eb3d086849acdf4a4d64157334b21a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 4 Mar 2008 21:38:53 +0000 Subject: Make alsa plugin to detect disconnection of devices. --- audio/pcm_bluetooth.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 2282b2ed..13550663 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -750,6 +750,11 @@ static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, return 0; } +static int bluetooth_playback_poll_descriptors_count(snd_pcm_ioplug_t *io) +{ + return 2; +} + static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int space) { @@ -759,14 +764,17 @@ static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io, assert(data->pipefd[0] >= 0); - if (space < 1) + if (space < 2) return 0; pfd[0].fd = data->pipefd[0]; pfd[0].events = POLLIN; pfd[0].revents = 0; + pfd[1].fd = data->stream.fd; + pfd[1].events = POLLERR | POLLHUP | POLLNVAL; + pfd[1].revents = 0; - return 1; + return 2; } static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, @@ -779,14 +787,19 @@ static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, DBG(""); assert(pfds); - assert(nfds == 1); + assert(nfds == 2); assert(revents); assert(pfds[0].fd >= 0); + assert(pfds[1].fd >= 0); if (io->state != SND_PCM_STATE_PREPARED) ret = read(pfds[0].fd, buf, 1); - *revents = (pfds[0].revents & ~POLLIN) | POLLOUT; + if (pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) + io->state = SND_PCM_STATE_DISCONNECTED; + + revents[0] = (pfds[0].revents & ~POLLIN) | POLLOUT; + revents[1] = (pfds[1].revents & ~POLLIN); return 0; } @@ -1087,6 +1100,7 @@ static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { .hw_params = bluetooth_hsp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_hsp_write, + .poll_descriptors_count = bluetooth_playback_poll_descriptors_count, .poll_descriptors = bluetooth_playback_poll_descriptors, .poll_revents = bluetooth_playback_poll_revents, .delay = bluetooth_playback_delay, @@ -1112,6 +1126,7 @@ static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { .hw_params = bluetooth_a2dp_hw_params, .prepare = bluetooth_prepare, .transfer = bluetooth_a2dp_write, + .poll_descriptors_count = bluetooth_playback_poll_descriptors_count, .poll_descriptors = bluetooth_playback_poll_descriptors, .poll_revents = bluetooth_playback_poll_revents, .delay = bluetooth_playback_delay, -- 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/a2dp.c | 2 +- audio/audio.conf | 3 +++ audio/avdtp.c | 22 +++++++++++++++++++--- audio/avdtp.h | 2 +- audio/control.c | 22 +++++++++++++++++++--- audio/control.h | 2 +- audio/manager.c | 46 +++++++++++++++++++++++++++++++--------------- 7 files changed, 75 insertions(+), 24 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index fd3cb632..53922513 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -1100,7 +1100,7 @@ int a2dp_init(DBusConnection *conn, GKeyFile *config) proceed: connection = dbus_connection_ref(conn); - avdtp_init(); + avdtp_init(config); if (source) { for (i = 0; i < sbc_srcs; i++) diff --git a/audio/audio.conf b/audio/audio.conf index 07ed3ea4..393d9c20 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -4,6 +4,9 @@ # particular interface [General] +# Switch to master role for incoming connections (defaults to true) +#ForceMaster=true + # If we want to disable support for specific services # Defaults to supporting all implemented services #Disable=Control,Source diff --git a/audio/avdtp.c b/audio/avdtp.c index 09e2b41a..22e042d4 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2874,7 +2874,7 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } -static GIOChannel *avdtp_server_socket(void) +static GIOChannel *avdtp_server_socket(gboolean master) { int sock, lm; struct sockaddr_l2 addr; @@ -2887,6 +2887,10 @@ static GIOChannel *avdtp_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("AVDTP server setsockopt: %s (%d)", strerror(errno), errno); @@ -2979,12 +2983,24 @@ void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst) bacpy(dst, &session->dst); } -int avdtp_init(void) +int avdtp_init(GKeyFile *config) { + GError *err = NULL; + gboolean tmp, master = TRUE; + if (avdtp_server) return 0; - avdtp_server = avdtp_server_socket(); + 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; + + avdtp_server = avdtp_server_socket(master); if (!avdtp_server) return -1; diff --git a/audio/avdtp.h b/audio/avdtp.h index 4ae17508..4e938b6d 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -264,5 +264,5 @@ int avdtp_error_posix_errno(struct avdtp_error *err); void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); -int avdtp_init(void); +int avdtp_init(GKeyFile *config); void avdtp_exit(void); 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; diff --git a/audio/control.h b/audio/control.h index 7247949d..4f3b7e00 100644 --- a/audio/control.h +++ b/audio/control.h @@ -24,7 +24,7 @@ #define AUDIO_CONTROL_INTERFACE "org.bluez.audio.Control" -int avrcp_init(DBusConnection *conn); +int avrcp_init(DBusConnection *conn, GKeyFile *config); void avrcp_exit(void); gboolean avrcp_connect(struct device *dev); diff --git a/audio/manager.c b/audio/manager.c index c5f7df30..b59e9dbf 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1485,7 +1485,7 @@ failed: return TRUE; } -static GIOChannel *server_socket(uint8_t *channel) +static GIOChannel *server_socket(uint8_t *channel, gboolean master) { int sock, lm; struct sockaddr_rc addr; @@ -1499,6 +1499,10 @@ static GIOChannel *server_socket(uint8_t *channel) } lm = RFCOMM_LM_SECURE; + + if (master) + lm |= RFCOMM_LM_MASTER; + if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { error("server setsockopt: %s (%d)", strerror(errno), errno); close(sock); @@ -1540,14 +1544,36 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; sdp_buf_t buf; - gboolean hfp = TRUE; + gboolean hfp = TRUE, master = TRUE; GError *err = NULL; uint32_t features; if (!(enabled.headset || enabled.gateway)) return 0; - hs_server = server_socket(&chan); + if (config) { + gboolean tmp; + + 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; + + tmp = g_key_file_get_boolean(config, "Headset", "DisableHFP", + &err); + if (err) { + debug("audio.conf: %s", err->message); + g_error_free(err); + err = NULL; + } else + hfp = tmp; + } + + hs_server = server_socket(&chan, master); if (!hs_server) return -1; @@ -1568,22 +1594,12 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) ag_io_cb, NULL); - if (config) { - hfp = g_key_file_get_boolean(config, "Headset", "EnableHFP", - &err); - if (err) { - debug("audio.conf: %s", err->message); - g_error_free(err); - err = NULL; - } - } - if (!hfp) return 0; chan = DEFAULT_HF_AG_CHANNEL; - hf_server = server_socket(&chan); + hf_server = server_socket(&chan, master); if (!hf_server) return -1; @@ -1697,7 +1713,7 @@ int audio_init(DBusConnection *conn, GKeyFile *config) goto failed; } - if (enabled.control && avrcp_init(conn) < 0) + if (enabled.control && avrcp_init(conn, config) < 0) goto failed; if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, -- cgit From 5db61cea279f4aae935b073dca99bb6ba37d4d4a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 5 Mar 2008 17:45:44 +0000 Subject: Use HFP instead of EnableHFP --- audio/audio.conf | 2 +- audio/manager.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/audio.conf b/audio/audio.conf index 393d9c20..4ae2dacc 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -21,7 +21,7 @@ # Set to true to support HFP (in addition to HSP only which is the default) # Defaults to false -EnableHFP=false +HFP=false # HFP Gateway features # Defaults to false diff --git a/audio/manager.c b/audio/manager.c index b59e9dbf..461090a3 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1563,7 +1563,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) } else master = tmp; - tmp = g_key_file_get_boolean(config, "Headset", "DisableHFP", + tmp = g_key_file_get_boolean(config, "Headset", "HFP", &err); if (err) { debug("audio.conf: %s", err->message); -- 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/audio.conf | 2 +- audio/avdtp.c | 2 +- audio/control.c | 2 +- audio/manager.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/audio.conf b/audio/audio.conf index 4ae2dacc..7f760ea6 100644 --- a/audio/audio.conf +++ b/audio/audio.conf @@ -5,7 +5,7 @@ [General] # Switch to master role for incoming connections (defaults to true) -#ForceMaster=true +#Master=true # If we want to disable support for specific services # Defaults to supporting all implemented services diff --git a/audio/avdtp.c b/audio/avdtp.c index 22e042d4..a92d1281 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2991,7 +2991,7 @@ int avdtp_init(GKeyFile *config) if (avdtp_server) 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); 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); diff --git a/audio/manager.c b/audio/manager.c index 461090a3..c0f4ffd1 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1554,7 +1554,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (config) { gboolean tmp; - 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') 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 714eda55e1af76786f134620b9c4291dee3350e2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 7 Mar 2008 19:02:55 +0000 Subject: Fix stream reconfiguration when it requires changing the stream endpoint. --- audio/a2dp.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index 53922513..e9c53c41 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -728,15 +728,29 @@ static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, return TRUE; } -static gboolean reconfigure(gpointer data) +static gboolean a2dp_reconfigure(gpointer data) { struct a2dp_setup *setup = data; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_cap = NULL; + GSList *l; int posix_err; + for (l = setup->client_caps; l != NULL; l = l->next) { + cap = l->data; + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec_cap = (void *) cap->data; + break; + } + posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK, - AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, + codec_cap->media_type, + codec_cap->media_codec_type, &lsep, &rsep); if (posix_err < 0) { error("No matching ACP and INT SEPs found"); @@ -784,7 +798,7 @@ static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, } if (setup->reconfigure) - g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup); + g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup); } static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, @@ -1193,7 +1207,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, struct a2dp_setup_cb *cb_data; GSList *l; struct a2dp_setup *setup; - struct a2dp_sep *sep = NULL; + struct a2dp_sep *sep = NULL, *tmp; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; struct avdtp_service_capability *cap; @@ -1214,7 +1228,7 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, return 0; for (l = sources; l != NULL; l = l->next) { - struct a2dp_sep *tmp = l->data; + tmp = l->data; if (tmp->locked) continue; @@ -1255,6 +1269,22 @@ unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: + for (l = sources; l != NULL; l = l->next) { + tmp = l->data; + + if (avdtp_has_stream(session, tmp->stream)) + break; + } + + if (l != NULL) { + setup->reconfigure = TRUE; + if (avdtp_close(session, tmp->stream) < 0) { + error("avdtp_close failed"); + goto failed; + } + break; + } + if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, codec_cap->media_type, codec_cap->media_codec_type, -- cgit From 48cd630295b689720a564ab8a03c0473930c63d6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 12 Mar 2008 14:34:13 +0000 Subject: headset_config_init should be called even if HFP isn't enabled (e.g. to get the SCORouting setting) --- audio/manager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index c0f4ffd1..4abc4571 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1594,6 +1594,8 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) ag_io_cb, NULL); + features = headset_config_init(config); + if (!hfp) return 0; @@ -1603,8 +1605,6 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (!hf_server) return -1; - features = headset_config_init(config); - if (hfp_ag_record(&buf, chan, features) < 0) { error("Unable to allocate new service record"); return -1; -- cgit From 1152fc72d35de616d5d2d3a29525fd6aacb852f9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 27 Mar 2008 14:05:37 +0000 Subject: Remove blocking call of FinishRemoteServiceTransaction. --- audio/device.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 2f5bf417..7510d9a4 100644 --- a/audio/device.c +++ b/audio/device.c @@ -380,8 +380,7 @@ int device_remove_stored(struct device *dev) void device_finish_sdp_transaction(struct device *dev) { char address[18], *addr_ptr = address; - DBusMessage *msg, *reply; - DBusError derr; + DBusMessage *msg; ba2str(&dev->dst, address); @@ -396,21 +395,7 @@ void device_finish_sdp_transaction(struct device *dev) dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_INVALID); - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(dev->conn, - msg, -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("FinishRemoteServiceTransaction(%s) failed: %s", - address, derr.message); - dbus_error_free(&derr); - return; - } - - dbus_message_unref(reply); + send_message_and_unref(dev->conn, msg); } #if 0 -- 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/Makefile.am | 32 ++++------ audio/a2dp.c | 55 ++++++++--------- audio/control.c | 89 ++++++++++++--------------- audio/device.c | 57 +---------------- audio/main.c | 80 ++++++++---------------- audio/manager.c | 178 +++++++++++++----------------------------------------- audio/manager.h | 6 +- 7 files changed, 149 insertions(+), 348 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index e36c2c2d..239d78be 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,23 +1,20 @@ if AUDIOSERVICE -if CONFIGFILES -confdir = $(sysconfdir)/bluetooth +plugindir = $(libdir)/bluetooth/plugins -conf_DATA = audio.service audio.conf -endif - -servicedir = $(libdir)/bluetooth - -service_PROGRAMS = bluetoothd-service-audio +plugin_LTLIBRARIES = libaudio.la -bluetoothd_service_audio_SOURCES = main.c \ - manager.h manager.c headset.h headset.c ipc.h ipc.c unix.h unix.c \ +libaudio_la_SOURCES = main.c \ + manager.h manager.c headset.h headset.c \ + ipc.h ipc.c unix.h unix.c \ device.h device.c gateway.h gateway.c \ - sink.c sink.h avdtp.c avdtp.h a2dp.c a2dp.h control.c control.h - -bluetoothd_service_audio_LDADD = $(top_builddir)/common/libhelper.a \ - @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ + sink.c sink.h avdtp.c avdtp.h \ + a2dp.c a2dp.h control.c control.h +libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc +libaudio_la_LIBADD = $(top_builddir)/common/libhelper.a \ + @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +libaudio_la_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ if ALSA alsadir = $(libdir)/alsa-lib @@ -54,11 +51,8 @@ libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ - -INCLUDES = -I$(top_srcdir)/common +INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/hcid -I$(top_srcdir)/sdpd -EXTRA_DIST = audio.service audio.conf audio-api.txt test-audio asound.conf +EXTRA_DIST = audio.conf audio-api.txt test-audio asound.conf MAINTAINERCLEANFILES = Makefile.in - diff --git a/audio/a2dp.c b/audio/a2dp.c index e9c53c41..c3292fef 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -42,6 +42,7 @@ #include "avdtp.h" #include "sink.h" #include "a2dp.h" +#include "sdpd.h" /* The duration that streams without users are allowed to stay in * STREAMING state. */ @@ -915,31 +916,32 @@ static struct avdtp_sep_ind mpeg_ind = { .reconfigure = reconf_ind }; -static int a2dp_source_record(sdp_buf_t *buf) +static sdp_record_t *a2dp_source_record() { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avdtp, a2src; 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 = AVDTP_UUID, ver = 0x0100, 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); sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID); svclass_id = sdp_list_append(0, &a2src); - sdp_set_service_classes(&record, svclass_id); + sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(&record, pfseq); + sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap); @@ -954,17 +956,12 @@ static int a2dp_source_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); features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); - - sdp_set_info_attr(&record, "Audio Source", 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, "Audio Source", 0, 0); free(psm); free(version); @@ -975,15 +972,13 @@ static int a2dp_source_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 a2dp_sink_record(sdp_buf_t *buf) +static sdp_record_t *a2dp_sink_record() { - return -1; + return NULL; } static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, @@ -991,9 +986,9 @@ static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, { struct a2dp_sep *sep; GSList **l; - int (*create_record)(sdp_buf_t *buf); + sdp_record_t *(*create_record)(void); uint32_t *record_id; - sdp_buf_t buf; + sdp_record_t *record; struct avdtp_sep_ind *ind; sep = g_new0(struct a2dp_sep, 1); @@ -1022,22 +1017,22 @@ static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, if (*record_id != 0) goto add; - memset(&buf, 0, sizeof(buf)); - if (create_record(&buf) < 0) { + record = create_record(); + if (!record) { error("Unable to allocate new service record"); avdtp_unregister_sep(sep->sep); g_free(sep); return NULL; } - *record_id = add_service_record(conn, &buf); - free(buf.data); - if (!*record_id) { - error("Unable to register A2DP service record"); + if (add_record_to_server(BDADDR_ANY, record) < 0) { + error("Unable to register A2DP service record");\ + sdp_record_free(record); avdtp_unregister_sep(sep->sep); g_free(sep); return NULL; } + *record_id = record->handle; add: *l = g_slist_append(*l, sep); @@ -1156,12 +1151,12 @@ void a2dp_exit() sources = NULL; if (source_record_id) { - remove_service_record(connection, source_record_id); + remove_record_from_server(source_record_id); source_record_id = 0; } if (sink_record_id) { - remove_service_record(connection, sink_record_id); + remove_record_from_server(sink_record_id); sink_record_id = 0; } 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; diff --git a/audio/device.c b/audio/device.c index 7510d9a4..7e25fe90 100644 --- a/audio/device.c +++ b/audio/device.c @@ -218,60 +218,6 @@ static void device_unregister(DBusConnection *conn, void *data) device_free(device); } -char *find_adapter(DBusConnection *conn, bdaddr_t *src) -{ - DBusMessage *msg, *reply; - DBusError derr; - char address[18], *addr_ptr = address; - char *path, *ret; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Manager", - "FindAdapter"); - if (!msg) { - error("Unable to allocate new method call"); - return NULL; - } - - ba2str(src, address); - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, - &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("FindAdapter(%s) failed: %s", address, derr.message); - dbus_error_free(&derr); - return NULL; - } - - dbus_error_init(&derr); - dbus_message_get_args(reply, &derr, - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Unable to get message args"); - dbus_message_unref(reply); - dbus_error_free(&derr); - return FALSE; - } - - ret = g_strdup(path); - - dbus_message_unref(reply); - - debug("Got path %s for adapter with address %s", ret, address); - - return ret; -} - struct device *device_register(DBusConnection *conn, const char *path, bdaddr_t *bda) { @@ -289,7 +235,8 @@ struct device *device_register(DBusConnection *conn, dev = g_new0(struct device, 1); - dev->adapter_path = find_adapter(conn, &src); + /* FIXME just to maintain compatibility */ + dev->adapter_path = g_strdup_printf("/org/bluez/hci%d", dev_id); if (!dev->adapter_path) { device_free(dev); return NULL; diff --git a/audio/main.c b/audio/main.c index 6c6b0d79..f96b19f8 100644 --- a/audio/main.c +++ b/audio/main.c @@ -26,29 +26,21 @@ #include #endif -#include -#include -#include -#include +#include +#include +#include +#include #include #include -#include -#include +#include "plugin.h" #include "dbus.h" #include "logging.h" #include "unix.h" #include "device.h" #include "manager.h" -static GMainLoop *main_loop = NULL; - -static void sig_term(int sig) -{ - g_main_loop_quit(main_loop); -} - static GKeyFile *load_config_file(const char *file) { GError *err = NULL; @@ -66,64 +58,42 @@ static GKeyFile *load_config_file(const char *file) return keyfile; } -int main(int argc, char *argv[]) +static DBusConnection *conn; + +static int audio_init(void) { - DBusConnection *conn; - struct sigaction sa; GKeyFile *config; - start_logging("audio", "Bluetooth Audio daemon"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - enable_debug(); + conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); + if (conn == NULL) + return -EIO; config = load_config_file(CONFIGDIR "/audio.conf"); - main_loop = g_main_loop_new(NULL, FALSE); - - conn = dbus_bus_system_setup_with_main_loop(NULL, NULL, NULL); - if (!conn) { - g_main_loop_unref(main_loop); - exit(1); - } - if (unix_init() < 0) { error("Unable to setup unix socket"); - exit(1); + return -EIO; } - if (audio_init(conn, config) < 0) { - error("Audio init failed!"); - exit(1); + if (audio_manager_init(conn, config) < 0) { + dbus_connection_unref(conn); + return -EIO; } - if (argc > 1 && !strcmp(argv[1], "-s")) - register_external_service(conn, "audio", "Audio service", ""); - if (config) - g_key_file_free(config); + g_key_file_free(config); + + register_external_service(conn, "audio", "audio service", ""); - g_main_loop_run(main_loop); + return 0; +} - audio_exit(); +static void audio_exit(void) +{ + audio_manager_exit(); unix_exit(); dbus_connection_unref(conn); - - g_main_loop_unref(main_loop); - - info("Exit"); - - stop_logging(); - - return 0; } + +BLUETOOTH_PLUGIN_DEFINE("audio", audio_init, audio_exit) diff --git a/audio/manager.c b/audio/manager.c index 4abc4571..7da74be6 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -62,6 +62,7 @@ #include "sink.h" #include "control.h" #include "manager.h" +#include "sdpd.h" typedef enum { HEADSET = 1 << 0, @@ -1164,33 +1165,34 @@ static void manager_unregister(DBusConnection *conn, void *data) } } -static int hsp_ag_record(sdp_buf_t *buf, uint8_t ch) +static sdp_record_t *hsp_ag_record(uint8_t ch) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; + sdp_record_t *record; sdp_list_t *aproto, *proto[2]; - sdp_record_t record; sdp_data_t *channel; - int ret; - 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); sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); svclass_id = sdp_list_append(0, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); + sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); profile.version = 0x0100; pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); + sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap_uuid); @@ -1203,14 +1205,9 @@ static int hsp_ag_record(sdp_buf_t *buf, uint8_t ch) 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); - sdp_set_info_attr(&record, "Headset Audio Gateway", 0, 0); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; + sdp_set_info_attr(record, "Headset Audio Gateway", 0, 0); sdp_data_free(channel); sdp_list_free(proto[0], 0); @@ -1220,42 +1217,41 @@ static int hsp_ag_record(sdp_buf_t *buf, uint8_t ch) 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 hfp_ag_record(sdp_buf_t *buf, uint8_t ch, uint32_t feat) +static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; uuid_t l2cap_uuid, rfcomm_uuid; sdp_profile_desc_t profile; sdp_list_t *aproto, *proto[2]; - sdp_record_t record; + sdp_record_t *record; sdp_data_t *channel, *features; uint8_t netid = 0x01; uint16_t sdpfeat; sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid); - int ret; - 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); sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); svclass_id = sdp_list_append(0, &svclass_uuid); sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(&record, svclass_id); + sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); profile.version = 0x0105; pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(&record, pfseq); + sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap_uuid); @@ -1269,19 +1265,14 @@ static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch, uint32_t feat) sdpfeat = (uint16_t) feat & 0xF; features = sdp_data_alloc(SDP_UINT16, &sdpfeat); - sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(&record, aproto); + sdp_set_access_protos(record, aproto); - sdp_set_info_attr(&record, "Hands-Free Audio Gateway", 0, 0); + sdp_set_info_attr(record, "Hands-Free Audio Gateway", 0, 0); - sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network); - - if (sdp_gen_record_pdu(&record, buf) < 0) - ret = -1; - else - ret = 0; + sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); sdp_data_free(channel); sdp_list_free(proto[0], 0); @@ -1291,93 +1282,8 @@ static int hfp_ag_record(sdp_buf_t *buf, uint8_t ch, uint32_t feat) 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; -} - -uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf) -{ - DBusMessage *msg, *reply; - DBusError derr; - dbus_uint32_t rec_id; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "AddServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &buf->data, buf->data_size, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, - msg, -1, &derr); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&derr) || - dbus_set_error_from_message(&derr, reply)) { - error("Adding service record failed: %s", derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_get_args(reply, &derr, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error("Invalid arguments to AddServiceRecord reply: %s", - derr.message); - dbus_message_unref(reply); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - debug("add_service_record: got record id 0x%x", rec_id); - - return rec_id; -} - -int remove_service_record(DBusConnection *conn, uint32_t rec_id) -{ - DBusMessage *msg, *reply; - DBusError derr; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RemoveServiceRecord"); - if (!msg) { - error("Can't allocate new method call"); - return 0; - } - - dbus_message_append_args(msg, DBUS_TYPE_UINT32, &rec_id, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(connection, - msg, -1, &derr); - - dbus_message_unref(msg); - if (dbus_error_is_set(&derr)) { - error("Removing service record 0x%x failed: %s", - rec_id, derr.message); - dbus_error_free(&derr); - return 0; - } - - dbus_message_unref(reply); - - return 0; + return record; } static void auth_cb(DBusPendingCall *call, void *data) @@ -1543,7 +1449,7 @@ static GIOChannel *server_socket(uint8_t *channel, gboolean master) static int headset_server_init(DBusConnection *conn, GKeyFile *config) { uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_buf_t buf; + sdp_record_t *record; gboolean hfp = TRUE, master = TRUE; GError *err = NULL; uint32_t features; @@ -1577,19 +1483,20 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (!hs_server) return -1; - if (hsp_ag_record(&buf, chan) < 0) { + record = hsp_ag_record(chan); + if (!record) { error("Unable to allocate new service record"); return -1; } - hs_record_id = add_service_record(conn, &buf); - free(buf.data); - if (!hs_record_id) { + if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register HS AG service record"); + sdp_record_free(record); g_io_channel_unref(hs_server); hs_server = NULL; return -1; } + hs_record_id = record->handle; g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) ag_io_cb, NULL); @@ -1605,19 +1512,20 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (!hf_server) return -1; - if (hfp_ag_record(&buf, chan, features) < 0) { + record = hfp_ag_record(chan, features); + if (!record) { error("Unable to allocate new service record"); return -1; } - hf_record_id = add_service_record(conn, &buf); - free(buf.data); - if (!hf_record_id) { - error("Unable to register HS AG service record"); + if (add_record_to_server(BDADDR_ANY, record) < 0) { + error("Unable to register HF AG service record"); + sdp_record_free(record); g_io_channel_unref(hf_server); - hs_server = NULL; + hf_server = NULL; return -1; } + hf_record_id = record->handle; g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) ag_io_cb, NULL); @@ -1628,7 +1536,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) static void server_exit(void) { if (hs_record_id) { - remove_service_record(connection, hs_record_id); + remove_record_from_server(hs_record_id); hs_record_id = 0; } @@ -1638,7 +1546,7 @@ static void server_exit(void) } if (hf_record_id) { - remove_service_record(connection, hf_record_id); + remove_record_from_server(hf_record_id); hf_record_id = 0; } @@ -1648,7 +1556,7 @@ static void server_exit(void) } } -int audio_init(DBusConnection *conn, GKeyFile *config) +int audio_manager_init(DBusConnection *conn, GKeyFile *config) { char *str; GError *err = NULL; @@ -1731,11 +1639,11 @@ int audio_init(DBusConnection *conn, GKeyFile *config) return 0; failed: - audio_exit(); + audio_manager_exit(); return -1; } -void audio_exit(void) +void audio_manager_exit(void) { server_exit(); diff --git a/audio/manager.h b/audio/manager.h index 4038d3b9..c8f826ee 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -36,11 +36,9 @@ struct enabled_interfaces { typedef void (*create_dev_cb_t) (struct device *dev, void *user_data); -int audio_init(DBusConnection *conn, GKeyFile *config); -void audio_exit(void); +int audio_manager_init(DBusConnection *conn, GKeyFile *config); +void audio_manager_exit(void); -uint32_t add_service_record(DBusConnection *conn, sdp_buf_t *buf); -int remove_service_record(DBusConnection *conn, uint32_t rec_id); gboolean server_is_enabled(uint16_t svc); struct device *manager_find_device(bdaddr_t *bda, const char *interface, -- cgit From 2fff5cdc8c0d7a626890ee8f0dd82d464e4fd375 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 28 Mar 2008 13:00:18 +0000 Subject: Remove blocking calls to GetRemoteName. --- audio/device.c | 43 ++++++++----------------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 7e25fe90..0debe220 100644 --- a/audio/device.c +++ b/audio/device.c @@ -73,44 +73,17 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, return send_message_and_unref(conn, reply); } -static char *get_dev_name(DBusConnection *conn, const char *adapter_path, - bdaddr_t *bda) +static char *get_dev_name(DBusConnection *conn, bdaddr_t *src, bdaddr_t *bda) { - DBusMessage *msg, *reply; - DBusError derr; - const char *name; - char address[18], *addr_ptr = address, *ret; - - msg = dbus_message_new_method_call("org.bluez", adapter_path, - "org.bluez.Adapter", "GetRemoteName"); - if (!msg) - return NULL; - - ba2str(bda, address); - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_INVALID); - - dbus_error_init(&derr); - reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, - &derr); - dbus_message_unref(msg); + char address[18], filename[PATH_MAX + 1]; - if (dbus_error_is_set(&derr)) { - error("%s GetRemoteName(): %s", adapter_path, derr.message); - dbus_error_free(&derr); - return NULL; - } - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) - return NULL; - - ret = g_strdup(name); + ba2str(src, address); - dbus_message_unref(reply); + /* check if it is in the cache */ + create_name(filename, PATH_MAX, STORAGEDIR, address, "names"); - return ret; + ba2str(bda, address); + return textfile_caseget(filename, address); } static DBusHandlerResult device_get_name(DBusConnection *conn, @@ -257,7 +230,7 @@ struct device *device_register(DBusConnection *conn, return NULL; } - dev->name = get_dev_name(conn, dev->adapter_path, bda); + dev->name = get_dev_name(conn, &src, bda); dev->path = g_strdup(path); bacpy(&dev->dst, bda); bacpy(&dev->src, &src); -- cgit From eb897867c1cc4802c168d3ca11a0e26b897bcb01 Mon Sep 17 00:00:00 2001 From: Vinicius Gomes Date: Fri, 28 Mar 2008 14:11:18 +0000 Subject: audio: Fixing the Makefile.am so it can compile on 64 bits machines. --- audio/Makefile.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 239d78be..52dcd0ae 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -12,7 +12,9 @@ libaudio_la_SOURCES = main.c \ a2dp.c a2dp.h control.c control.h libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc -libaudio_la_LIBADD = $(top_builddir)/common/libhelper.a \ +# libaudio_la_LIBADD = $(top_builddir)/common/libhelper.a \ +# @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ +LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ libaudio_la_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ if ALSA -- cgit From e50eaea0cca7cb5b95a67b5ea9eee512b2b5485e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 28 Mar 2008 20:44:39 +0000 Subject: Update the fix for the 64-bit build issue --- audio/Makefile.am | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 52dcd0ae..37d7a98f 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -11,12 +11,9 @@ libaudio_la_SOURCES = main.c \ sink.c sink.h avdtp.c avdtp.h \ a2dp.c a2dp.h control.c control.h -libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc -# libaudio_la_LIBADD = $(top_builddir)/common/libhelper.a \ -# @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -libaudio_la_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ + if ALSA alsadir = $(libdir)/alsa-lib @@ -53,6 +50,10 @@ libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif +AM_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc + +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ + INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/hcid -I$(top_srcdir)/sdpd EXTRA_DIST = audio.conf audio-api.txt test-audio asound.conf -- cgit From 34b8fd54ca89ff67e3f319747dcf3cf8b9e305f5 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 28 Mar 2008 20:46:51 +0000 Subject: Fix service description --- audio/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index f96b19f8..b2566c95 100644 --- a/audio/main.c +++ b/audio/main.c @@ -82,7 +82,7 @@ static int audio_init(void) g_key_file_free(config); - register_external_service(conn, "audio", "audio service", ""); + register_external_service(conn, "audio", "Audio service", ""); return 0; } -- cgit From ea56c31528d8fd2b2aba3e9645f64725681cf244 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 28 Mar 2008 20:53:12 +0000 Subject: Fix linking issue with exported symbols --- audio/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 37d7a98f..841617ea 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -11,6 +11,8 @@ libaudio_la_SOURCES = main.c \ sink.c sink.h avdtp.c avdtp.h \ a2dp.c a2dp.h control.c control.h +libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc + LDADD = $(top_builddir)/common/libhelper.a \ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ @@ -50,8 +52,6 @@ libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif -AM_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc - AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/hcid -I$(top_srcdir)/sdpd -- cgit From 02643f01b8da5fa59a1af4dfbd055f11033e353c Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 1 Apr 2008 21:23:15 +0000 Subject: Fix emitting errors to already disconnected clients. --- audio/unix.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'audio') diff --git a/audio/unix.c b/audio/unix.c index d81291c7..bbb58d72 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -163,6 +163,9 @@ static void unix_ipc_error(struct unix_client *client, int type, int err) char buf[BT_AUDIO_IPC_PACKET_SIZE]; bt_audio_rsp_msg_header_t *rsp_hdr = (void *) buf; + if (!g_slist_find(clients, client)) + return; + memset(buf, 0, sizeof(buf)); rsp_hdr->msg_h.msg_type = type; rsp_hdr->posix_errno = err; -- cgit From f390a1c27da2b89f521960dfe9945bcc9039caa6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 14 Apr 2008 14:41:42 +0000 Subject: Use internal call when possible to authorize incoming connections. --- audio/manager.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 7da74be6..869557ca 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -63,6 +63,7 @@ #include "control.h" #include "manager.h" #include "sdpd.h" +#include "plugin.h" typedef enum { HEADSET = 1 << 0, @@ -1286,11 +1287,9 @@ static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat) return record; } -static void auth_cb(DBusPendingCall *call, void *data) +static void auth_cb(DBusError *derr, void *user_data) { - struct device *device = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; + struct device *device = user_data; const char *uuid; if (get_hfp_active(device)) @@ -1298,14 +1297,14 @@ static void auth_cb(DBusPendingCall *call, void *data) else uuid = HSP_AG_UUID; - 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(&device->dst, uuid, NULL); + if (plugin_cancel_auth(&device->dst) < 0) + manager_cancel_authorize(&device->dst, uuid, + NULL); } - dbus_error_free(&err); headset_set_state(device, HEADSET_STATE_DISCONNECTED); } else { @@ -1318,6 +1317,17 @@ static void auth_cb(DBusPendingCall *call, void *data) debug("Accepted headset connection from %s for %s", hs_address, device->path); } +} + +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); } @@ -1378,9 +1388,14 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (!manager_authorize(&device->dst, uuid, auth_cb, device, NULL)) + if (plugin_req_auth(&device->src, &device->dst, uuid, auth_cb, + device) == 0) + goto proceed; + else if (!manager_authorize(&device->dst, uuid, auth_cb_old, device, + NULL)) goto failed; +proceed: headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); return TRUE; -- cgit From 05498649453b5059c3fc317394ad21b42a62a669 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 21 Apr 2008 12:49:16 +0000 Subject: First steps towards HSP HS role and Audio.Gateway D-Bus interface support --- audio/gateway.h | 3 + audio/manager.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 173 insertions(+), 33 deletions(-) (limited to 'audio') diff --git a/audio/gateway.h b/audio/gateway.h index a4245909..12e413fd 100644 --- a/audio/gateway.h +++ b/audio/gateway.h @@ -24,6 +24,9 @@ #define AUDIO_GATEWAY_INTERFACE "org.bluez.audio.Gateway" +#define DEFAULT_HSP_HS_CHANNEL 6 +#define DEFAULT_HFP_HS_CHANNEL 7 + int gateway_init(DBusConnection *conn, gboolean disable_hfp, gboolean sco_hci); void gateway_exit(void); diff --git a/audio/manager.c b/audio/manager.c index 869557ca..6154e766 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -103,11 +103,15 @@ static struct device *default_dev = NULL; static GSList *devices = NULL; -static uint32_t hs_record_id = 0; -static uint32_t hf_record_id = 0; +static uint32_t hsp_ag_record_id = 0; +static uint32_t hfp_ag_record_id = 0; -static GIOChannel *hs_server = NULL; -static GIOChannel *hf_server = NULL; +static uint32_t hsp_hs_record_id = 0; + +static GIOChannel *hsp_ag_server = NULL; +static GIOChannel *hfp_ag_server = NULL; + +static GIOChannel *hsp_hs_server = NULL; static struct enabled_interfaces enabled = { .headset = TRUE, @@ -227,10 +231,16 @@ gboolean server_is_enabled(uint16_t svc) switch (svc) { case HEADSET_SVCLASS_ID: - ret = (hs_server != NULL); + ret = (hsp_ag_server != NULL); + break; + case HEADSET_AGW_SVCLASS_ID: + ret = (hsp_hs_server != NULL); break; case HANDSFREE_SVCLASS_ID: - ret = (hf_server != NULL); + ret = (hfp_ag_server != NULL); + break; + case HANDSFREE_AGW_SVCLASS_ID: + ret = FALSE; break; case AUDIO_SINK_SVCLASS_ID: return enabled.sink; @@ -1222,6 +1232,62 @@ static sdp_record_t *hsp_ag_record(uint8_t ch) return record; } +static sdp_record_t *hsp_hs_record(uint8_t ch) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_record_t *record; + sdp_list_t *aproto, *proto[2]; + sdp_data_t *channel; + + 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_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID); + svclass_id = sdp_list_append(0, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(0, &profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(0, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(0, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Headset", 0, 0); + + sdp_data_free(channel); + 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); + + return record; +} + static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat) { sdp_list_t *svclass_id, *pfseq, *apseq, *root; @@ -1360,7 +1426,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (chan == hs_server) { + if (chan == hsp_ag_server) { hfp_active = FALSE; uuid = HSP_AG_UUID; } else { @@ -1406,6 +1472,12 @@ failed: return TRUE; } +static gboolean hs_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +{ + /*Stub*/ + return TRUE; +} + static GIOChannel *server_socket(uint8_t *channel, gboolean master) { int sock, lm; @@ -1469,7 +1541,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) GError *err = NULL; uint32_t features; - if (!(enabled.headset || enabled.gateway)) + if (!enabled.headset) return 0; if (config) { @@ -1494,8 +1566,8 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) hfp = tmp; } - hs_server = server_socket(&chan, master); - if (!hs_server) + hsp_ag_server = server_socket(&chan, master); + if (!hsp_ag_server) return -1; record = hsp_ag_record(chan); @@ -1507,14 +1579,15 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register HS AG service record"); sdp_record_free(record); - g_io_channel_unref(hs_server); - hs_server = NULL; + g_io_channel_unref(hsp_ag_server); + hsp_ag_server = NULL; return -1; } - hs_record_id = record->handle; + hsp_ag_record_id = record->handle; - g_io_add_watch(hs_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) ag_io_cb, NULL); + g_io_add_watch(hsp_ag_server, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) ag_io_cb, NULL); features = headset_config_init(config); @@ -1523,8 +1596,8 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) chan = DEFAULT_HF_AG_CHANNEL; - hf_server = server_socket(&chan, master); - if (!hf_server) + hfp_ag_server = server_socket(&chan, master); + if (!hfp_ag_server) return -1; record = hfp_ag_record(chan, features); @@ -1536,38 +1609,97 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) if (add_record_to_server(BDADDR_ANY, record) < 0) { error("Unable to register HF AG service record"); sdp_record_free(record); - g_io_channel_unref(hf_server); - hf_server = NULL; + g_io_channel_unref(hfp_ag_server); + hfp_ag_server = NULL; return -1; } - hf_record_id = record->handle; + hfp_ag_record_id = record->handle; - g_io_add_watch(hf_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + g_io_add_watch(hfp_ag_server, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc) ag_io_cb, NULL); return 0; } +static int gateway_server_init(DBusConnection *conn, GKeyFile *config) +{ + uint8_t chan = DEFAULT_HSP_HS_CHANNEL; + sdp_record_t *record; + gboolean master = TRUE; + GError *err = NULL; + + if (!enabled.gateway) + return 0; + + if (config) { + gboolean tmp; + + 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; + } + + hsp_hs_server = server_socket(&chan, master); + if (!hsp_hs_server) + return -1; + + record = hsp_hs_record(chan); + if (!record) { + error("Unable to allocate new service record"); + return -1; + } + + if (add_record_to_server(BDADDR_ANY, record) < 0) { + error("Unable to register HSP HS service record"); + sdp_record_free(record); + g_io_channel_unref(hsp_hs_server); + hsp_hs_server = NULL; + return -1; + } + hsp_hs_record_id = record->handle; + + g_io_add_watch(hsp_hs_server, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + (GIOFunc) hs_io_cb, NULL); + return 0; +} + static void server_exit(void) { - if (hs_record_id) { - remove_record_from_server(hs_record_id); - hs_record_id = 0; + if (hsp_ag_record_id) { + remove_record_from_server(hsp_ag_record_id); + hsp_ag_record_id = 0; } - if (hs_server) { - g_io_channel_unref(hs_server); - hs_server = NULL; + if (hsp_ag_server) { + g_io_channel_unref(hsp_ag_server); + hsp_ag_server = NULL; } - if (hf_record_id) { - remove_record_from_server(hf_record_id); - hf_record_id = 0; + if (hsp_hs_record_id) { + remove_record_from_server(hsp_hs_record_id); + hsp_hs_record_id = 0; } - if (hf_server) { - g_io_channel_unref(hf_server); - hf_server = NULL; + if (hsp_hs_server) { + g_io_channel_unref(hsp_hs_server); + hsp_hs_server = NULL; + } + + if (hfp_ag_record_id) { + remove_record_from_server(hfp_ag_record_id); + hfp_ag_record_id = 0; + } + + if (hfp_ag_server) { + g_io_channel_unref(hfp_ag_server); + hfp_ag_server = NULL; } } @@ -1631,6 +1763,11 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *config) goto failed; } + if (enabled.gateway) { + if (gateway_server_init(conn, config) < 0) + goto failed; + } + if (enabled.source || enabled.sink) { if (a2dp_init(conn, config) < 0) goto failed; -- cgit From 6c752ad5657d86f3ef900232e4ee8cfc115863a3 Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Fri, 25 Apr 2008 17:53:58 +0000 Subject: Fixed memory leak in headset stop function --- audio/headset.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 83752cdf..b2948da2 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1190,13 +1190,13 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, struct headset *hs = device->headset; DBusMessage *reply = NULL; + if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) + return error_not_connected(conn, msg); + reply = dbus_message_new_method_return(msg); if (!reply) return DBUS_HANDLER_RESULT_NEED_MEMORY; - if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) - return error_not_connected(conn, msg); - headset_set_state(device, HEADSET_STATE_CONNECTED); send_message_and_unref(conn, reply); -- cgit From 98d343aa97293d5316a56e894be5ab1840cc83a0 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 25 Apr 2008 18:10:07 +0000 Subject: Make use of convenient function bt_rfcomm_connect. --- audio/headset.c | 308 +++++++------------------------------------------------- 1 file changed, 35 insertions(+), 273 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index b2948da2..65e60e9e 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -57,6 +57,7 @@ #include "manager.h" #include "error.h" #include "headset.h" +#include "glib-helper.h" #define DC_TIMEOUT 3000 @@ -148,7 +149,7 @@ struct event { static int rfcomm_connect(struct device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id); -static int get_handles(struct device *device, headset_stream_cb_t cb, +static int get_records(struct device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id); static int headset_send(struct headset *hs, char *format, ...) @@ -733,33 +734,15 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, - struct device *dev) +static void rfcomm_connect_cb(GIOChannel *chan, int err, gpointer user_data) { - struct headset *hs; - struct pending_connect *p; + struct device *dev = user_data; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; char hs_address[18]; - int sk, ret; - socklen_t len; - - if (cond & G_IO_NVAL) - return FALSE; - - hs = dev->headset; - p = hs->pending; - sk = g_io_channel_unix_get_fd(chan); - - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - p->err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), p->err); - goto failed; - } - - if (ret != 0) { - p->err = ret; - error("connect(): %s (%d)", strerror(ret), ret); + if (err < 0) { + error("connect(): %s (%d)", strerror(-err), -err); goto failed; } @@ -779,7 +762,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, /* In HFP mode wait for Service Level Connection */ if (hs->hfp_active) - return FALSE; + return; headset_set_state(dev, HEADSET_STATE_CONNECTED); @@ -787,7 +770,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, p->err = sco_connect(dev, NULL, NULL, NULL); if (p->err < 0) goto failed; - return FALSE; + return; } if (p->msg) { @@ -797,7 +780,7 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, pending_connect_finalize(dev); - return FALSE; + return; failed: if (p->msg) @@ -807,56 +790,30 @@ failed: headset_set_state(dev, HEADSET_STATE_CONNECTED); else headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - - return FALSE; } -static void get_record_reply(DBusPendingCall *call, void *data) +static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) { - DBusMessage *reply; - DBusError derr; - uint8_t *array; - int array_len, record_len, err = EIO, ch = -1; + struct device *dev = user_data; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; + int ch = -1; sdp_record_t *record = NULL; sdp_list_t *protos, *classes = NULL; uuid_t uuid; - struct device *dev = data; - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - - reply = dbus_pending_call_steal_reply(call); - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("GetRemoteServiceRecord failed: %s", derr.message); - dbus_error_free(&derr); - goto failed_not_supported; - } - - dbus_error_init(&derr); - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &array, &array_len, - DBUS_TYPE_INVALID)) { - error("Unable to get args from GetRecordReply: %s", derr.message); - dbus_error_free(&derr); - goto failed_not_supported; - } - - if (!array) { - error("get_record_reply: Unable to get handle array from reply"); + if (err < 0) { + error("Unable to get service record: %s (%d)", strerror(-err), + -err); goto failed_not_supported; } - record = sdp_extract_pdu(array, &record_len); - if (!record) { - error("Unable to extract service record from reply"); + if (!recs || !recs->data) { + error("No records found"); goto failed_not_supported; } - if (record_len != array_len) - debug("warning: array len (%d) != record len (%d)", - array_len, record_len); + record = recs->data; if (sdp_get_service_classes(record, &classes) < 0) { error("Unable to get service classes from record"); @@ -909,9 +866,6 @@ static void get_record_reply(DBusPendingCall *call, void *data) sdp_list_free(classes, free); sdp_record_free(record); - dbus_message_unref(reply); - - device_finish_sdp_transaction(dev); return; @@ -926,157 +880,20 @@ failed: sdp_list_free(classes, free); if (record) sdp_record_free(record); - if (reply) - dbus_message_unref(reply); pending_connect_finalize(dev); headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - device_finish_sdp_transaction(dev); } -static void get_handles_reply(DBusPendingCall *call, void *data) -{ - DBusMessage *msg = NULL, *reply; - DBusPendingCall *pending; - DBusError derr; - struct device *dev = data; - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - char address[18], *addr_ptr = address; - dbus_uint32_t *array = NULL; - dbus_uint32_t handle; - int array_len; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("GetRemoteServiceHandles failed: %s", derr.message); - if (p->msg) { - if (dbus_error_has_name(&derr, - "org.bluez.Error.ConnectionAttemptFailed")) - error_connection_attempt_failed(dev->conn, p->msg, - EHOSTDOWN); - else - error_not_supported(dev->conn, p->msg); - } - dbus_error_free(&derr); - goto failed; - } - - dbus_error_init(&derr); - if (!dbus_message_get_args(reply, &derr, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &array, &array_len, - DBUS_TYPE_INVALID)) { - error("Unable to get args from reply: %s", derr.message); - dbus_error_free(&derr); - if (p->msg) - error_not_supported(dev->conn, p->msg); - goto failed; - } - - if (!array) { - error("get_handles_reply: Unable to get handle array from reply"); - if (p->msg) - error_not_supported(dev->conn, p->msg); - goto failed; - } - - if (array_len < 1) { - if (hs->search_hfp) { - debug("No record handles found for hfp"); - hs->search_hfp = FALSE; - get_handles(dev, NULL, NULL, NULL); - dbus_message_unref(reply); - return; - } - - debug("No record handles found for hsp"); - - if (p->msg) - error_not_supported(dev->conn, p->msg); - goto failed; - } - - if (array_len > 1) - debug("Multiple records found. Using the first one."); - - msg = dbus_message_new_method_call("org.bluez", dev->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceRecord"); - if (!msg) { - error("Unable to allocate new method call"); - if (p->msg) - error_out_of_memory(dev->conn, p->msg); - goto failed; - } - - ba2str(&dev->dst, address); - - handle = array[0]; - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_UINT32, &handle, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(dev->conn, msg, &pending, -1)) { - error("Sending GetRemoteServiceRecord failed"); - if (p->msg) - error_connection_attempt_failed(dev->conn, p->msg, EIO); - goto failed; - } - - dbus_pending_call_set_notify(pending, get_record_reply, dev, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); - dbus_message_unref(reply); - - return; - -failed: - if (msg) - dbus_message_unref(msg); - dbus_message_unref(reply); - p->err = EIO; - pending_connect_finalize(dev); - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); -} - -static int get_handles(struct device *device, headset_stream_cb_t cb, +static int get_records(struct device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { - DBusPendingCall *pending; struct headset *hs = device->headset; - const char *hs_svc; - const char *addr_ptr; - char hs_address[18]; - DBusMessage *msg; - - msg = dbus_message_new_method_call("org.bluez", device->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceHandles"); - if (!msg) { - error("Could not create a new dbus message"); - return -ENOMEM; - } - - if (hs->search_hfp) - hs_svc = "hfp"; - else - hs_svc = "hsp"; + uuid_t uuid; - ba2str(&device->dst, hs_address); - addr_ptr = hs_address; - dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_STRING, &hs_svc, - DBUS_TYPE_INVALID); + sdp_uuid16_create(&uuid, hs->search_hfp ? HANDSFREE_SVCLASS_ID : + HEADSET_SVCLASS_ID); headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); - if (!dbus_connection_send_with_reply(device->conn, msg, &pending, -1)) { - error("Sending GetRemoteServiceHandles failed"); - dbus_message_unref(msg); - return -EIO; - } pending_connect_init(hs, HEADSET_STATE_CONNECTED); @@ -1088,75 +905,30 @@ static int get_handles(struct device *device, headset_stream_cb_t cb, *cb_id = id; } - dbus_pending_call_set_notify(pending, get_handles_reply, device, NULL); - - if (hs->pending) - hs->pending->call = pending; - else - dbus_pending_call_unref(pending); - - dbus_message_unref(msg); - - return 0; + return bt_search_service(&device->src, &device->dst, &uuid, + get_record_cb, device, NULL); } static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { struct headset *hs = dev->headset; - struct sockaddr_rc addr; - GIOChannel *io; char address[18]; - int sk, err; + int err; if (hs->rfcomm_ch < 0) - return get_handles(dev, cb, user_data, cb_id); + return get_records(dev, cb, user_data, cb_id); ba2str(&dev->dst, address); debug("%s: Connecting to %s channel %d", dev->path, address, hs->rfcomm_ch); - sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) { - err = errno; - error("socket(BTPROTO_RFCOMM): %s (%d)", strerror(err), err); - return -err; - } - - io = g_io_channel_unix_new(sk); - if (!io) { - close(sk); - return -ENOMEM; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = 0; - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = errno; - error("bind: %s (%d)", strerror(errno), errno); - goto failed; - } - - if (set_nonblocking(sk) < 0) { - err = errno; - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, &dev->dst); - addr.rc_channel = hs->rfcomm_ch; - - err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); - - if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect() failed: %s (%d)", strerror(err), err); - goto failed; + err = bt_rfcomm_connect(&dev->src, &dev->dst, hs->rfcomm_ch, + rfcomm_connect_cb, dev); + if (err < 0) { + error("connect() failed: %s (%d)", strerror(-err), -err); + return err; } headset_set_state(dev, HEADSET_STATE_CONNECT_IN_PROGRESS); @@ -1170,17 +942,7 @@ static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb, *cb_id = id; } - g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) rfcomm_connect_cb, dev); - - hs->pending->io = io; - return 0; - -failed: - g_io_channel_close(io); - g_io_channel_unref(io); - return -err; } static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, -- cgit From 90ddd9872e42d11a347de4ec3f3ba47b66ca92e2 Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Fri, 25 Apr 2008 18:22:51 +0000 Subject: Make use of convenient function bt_sco_connect --- audio/headset.c | 115 ++++++++++++++------------------------------------------ 1 file changed, 29 insertions(+), 86 deletions(-) (limited to 'audio') diff --git a/audio/headset.c b/audio/headset.c index 65e60e9e..859a87ba 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -290,111 +290,64 @@ static unsigned int connect_cb_new(struct headset *hs, return cb->id; } -static gboolean sco_connect_cb(GIOChannel *chan, GIOCondition cond, - struct device *device) +static void sco_connect_cb(GIOChannel *chan, int err, + gpointer user_data) { - struct headset *hs; - int ret, sk; - socklen_t len; - struct pending_connect *p; + int sk; + struct device *dev = user_data; + struct headset *hs = dev->headset; + struct pending_connect *p = hs->pending; - if (cond & G_IO_NVAL) - return FALSE; + if (err < 0) { + error("connect(): %s (%d)", strerror(-err), -err); - hs = device->headset; - p = hs->pending; + if (p->msg) + error_connection_attempt_failed(dev->conn, p->msg, p->err); - sk = g_io_channel_unix_get_fd(chan); + pending_connect_finalize(dev); + if (hs->rfcomm) + headset_set_state(dev, HEADSET_STATE_CONNECTED); + else + headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - p->err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(p->err), - p->err); - goto failed; + return; } - if (ret != 0) { - p->err = ret; - error("connect(): %s (%d)", strerror(ret), ret); - goto failed; - } + debug("SCO socket opened for headset %s", dev->path); - debug("SCO socket opened for headset %s", device->path); + sk = g_io_channel_unix_get_fd(chan); info("SCO fd=%d", sk); hs->sco = chan; p->io = NULL; - pending_connect_finalize(device); - - fcntl(sk, F_SETFL, 0); - - headset_set_state(device, HEADSET_STATE_PLAYING); + if (p->msg) { + DBusMessage *reply = dbus_message_new_method_return(p->msg); + send_message_and_unref(dev->conn, reply); + } - return FALSE; + pending_connect_finalize(dev); -failed: - pending_connect_finalize(device); - if (hs->rfcomm) - headset_set_state(device, HEADSET_STATE_CONNECTED); - else - headset_set_state(device, HEADSET_STATE_DISCONNECTED); + fcntl(sk, F_SETFL, 0); - return FALSE; + headset_set_state(dev, HEADSET_STATE_PLAYING); } static int sco_connect(struct device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { struct headset *hs = dev->headset; - struct sockaddr_sco addr; - GIOChannel *io; - int sk, err; + int err; if (hs->state != HEADSET_STATE_CONNECTED) return -EINVAL; - sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); - if (sk < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); + err = bt_sco_connect(&dev->src, &dev->dst, sco_connect_cb, dev); + if (err < 0) { + error("connect: %s (%d)", strerror(-err), -err); return -err; } - io = g_io_channel_unix_new(sk); - if (!io) { - close(sk); - return -ENOMEM; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, BDADDR_ANY); - - if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - err = errno; - error("socket(BTPROTO_SCO): %s (%d)", strerror(err), err); - goto failed; - } - - if (set_nonblocking(sk) < 0) { - err = errno; - goto failed; - } - - memset(&addr, 0, sizeof(addr)); - addr.sco_family = AF_BLUETOOTH; - bacpy(&addr.sco_bdaddr, &dev->dst); - - err = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); - - if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) { - err = errno; - error("connect: %s (%d)", strerror(errno), errno); - goto failed; - } - headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS); pending_connect_init(hs, HEADSET_STATE_PLAYING); @@ -406,17 +359,7 @@ static int sco_connect(struct device *dev, headset_stream_cb_t cb, *cb_id = id; } - g_io_add_watch(io, G_IO_OUT | G_IO_NVAL | G_IO_ERR | G_IO_HUP, - (GIOFunc) sco_connect_cb, dev); - - hs->pending->io = io; - return 0; - -failed: - g_io_channel_close(io); - g_io_channel_unref(io); - return -err; } static void hfp_slc_complete(struct device *dev) -- cgit From 615594b3d9c8a403e4f6da8aaa3a3633cae679d8 Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Fri, 25 Apr 2008 21:49:44 +0000 Subject: Fixed remote headset connection state --- audio/manager.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 6154e766..d548bff6 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1382,6 +1382,7 @@ static void auth_cb(DBusError *derr, void *user_data) debug("Accepted headset connection from %s for %s", hs_address, device->path); + headset_set_state(device, HEADSET_STATE_CONNECTED); } } -- cgit From 87419d4f0e86f4ed66831bfeab77dde1f829455a Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Mon, 28 Apr 2008 19:47:29 +0000 Subject: AVDTP l2cap connection clean up in audio plugin --- audio/avdtp.c | 98 ++++++++++++----------------------------------------------- 1 file changed, 19 insertions(+), 79 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index a92d1281..3bab1e33 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -46,6 +46,7 @@ #include "manager.h" #include "control.h" #include "avdtp.h" +#include "glib-helper.h" #include @@ -1506,41 +1507,29 @@ failed: return FALSE; } -static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static void l2cap_connect_cb(GIOChannel *chan, int err, gpointer user_data) { - struct avdtp *session = data; + struct avdtp *session = user_data; struct l2cap_options l2o; socklen_t len; - int ret, err, sk; + int sk; char address[18]; - if (cond & G_IO_NVAL) - return FALSE; - if (!g_slist_find(sessions, session)) { debug("l2cap_connect_cb: session got removed"); - return FALSE; + return; } - sk = g_io_channel_unix_get_fd(chan); - - len = sizeof(ret); - if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { - err = errno; - error("getsockopt(SO_ERROR): %s (%d)", strerror(err), err); + if (err < 0) { + error("connect(): %s (%d)", strerror(-err), -err); goto failed; } - if (ret != 0) { - err = ret; - error("connect(): %s (%d)", strerror(err), err); - goto failed; - } + sk = g_io_channel_unix_get_fd(chan); - if (cond & G_IO_HUP) { - err = EIO; - goto failed; + if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { + session->sock = sk; + session->state = AVDTP_SESSION_STATE_CONNECTING; } ba2str(&session->dst, address); @@ -1583,11 +1572,9 @@ static gboolean l2cap_connect_cb(GIOChannel *chan, GIOCondition cond, process_queue(session); - return FALSE; + return; failed: - close(sk); - if (session->pending_open) { avdtp_sep_set_state(session, session->pending_open->lsep, AVDTP_STATE_IDLE); @@ -1595,67 +1582,20 @@ failed: } else connection_lost(session, -err); - return FALSE; + return; } static int l2cap_connect(struct avdtp *session) { - struct sockaddr_l2 l2a; - GIOChannel *io; - int sk; - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &session->src); - - sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if (sk < 0) { - error("Cannot create L2CAP socket. %s(%d)", strerror(errno), - errno); - return -errno; - } - - if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) { - error("Bind failed. %s (%d)", strerror(errno), errno); - return -errno; - } - - memset(&l2a, 0, sizeof(l2a)); - l2a.l2_family = AF_BLUETOOTH; - bacpy(&l2a.l2_bdaddr, &session->dst); - l2a.l2_psm = htobs(AVDTP_PSM); + int err; - if (set_nonblocking(sk) < 0) { - error("Set non blocking: %s (%d)", strerror(errno), errno); - return -errno; + err = bt_l2cap_connect(&session->src, &session->dst, htobs(AVDTP_PSM), + l2cap_connect_cb, session); + if (err < 0) { + error("Connect failed. %s(%d)", strerror(-err), -err); + return err; } - io = g_io_channel_unix_new(sk); - g_io_channel_set_close_on_unref(io, FALSE); - - 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); - return -errno; - } - - g_io_add_watch(io, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) l2cap_connect_cb, session); - - if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { - session->sock = sk; - session->state = AVDTP_SESSION_STATE_CONNECTING; - } - - - } else - l2cap_connect_cb(io, G_IO_OUT, session); - - g_io_channel_unref(io); - return 0; } -- cgit From 05302369d81c96d475b50d1feb002cd9a25804cf Mon Sep 17 00:00:00 2001 From: Cidorvan Leite Date: Tue, 29 Apr 2008 13:44:21 +0000 Subject: Fixed psm endianness for l2cap connection --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 3bab1e33..ec2a8c12 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1589,7 +1589,7 @@ static int l2cap_connect(struct avdtp *session) { int err; - err = bt_l2cap_connect(&session->src, &session->dst, htobs(AVDTP_PSM), + err = bt_l2cap_connect(&session->src, &session->dst, AVDTP_PSM, l2cap_connect_cb, session); if (err < 0) { error("Connect failed. %s(%d)", strerror(-err), -err); -- cgit From 7c2961d3277c279c77e6bcf9c7ec438fb2e4212b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 29 Apr 2008 20:37:12 +0000 Subject: Fix dbus calls to the same process. --- audio/manager.c | 238 ++++++++------------------------------------------------ 1 file changed, 33 insertions(+), 205 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index d548bff6..9211ccb7 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -64,6 +64,7 @@ #include "manager.h" #include "sdpd.h" #include "plugin.h" +#include "glib-helper.h" typedef enum { HEADSET = 1 << 0, @@ -87,7 +88,6 @@ struct audio_sdp_data { DBusMessage *msg; /* Method call or NULL */ - GSList *handles; /* uint32_t * */ GSList *records; /* sdp_record_t * */ audio_sdp_state_t state; @@ -121,9 +121,7 @@ static struct enabled_interfaces enabled = { .control = TRUE, }; -static void get_next_record(struct audio_sdp_data *data); -static DBusHandlerResult get_handles(const char *uuid, - struct audio_sdp_data *data); +static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data); static struct device *create_device(bdaddr_t *bda) { @@ -329,8 +327,6 @@ static void finish_sdp(struct audio_sdp_data *data, gboolean success) debug("Audio service discovery completed with %s", success ? "success" : "failure"); - device_finish_sdp_transaction(data->device); - if (!success) goto done; @@ -390,237 +386,66 @@ done: } if (data->msg) dbus_message_unref(data->msg); - g_slist_foreach(data->handles, (GFunc) g_free, NULL); - g_slist_free(data->handles); g_slist_foreach(data->records, (GFunc) sdp_record_free, NULL); g_slist_free(data->records); g_free(data); } -static void get_record_reply(DBusPendingCall *call, - struct audio_sdp_data *data) +static void get_records_cb(sdp_list_t *recs, int err, gpointer user_data) { - DBusMessage *reply; - DBusError derr; - uint8_t *array; - int array_len, record_len; - sdp_record_t *record; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - /* FIXME : forward error message as is */ - error("GetRemoteServiceRecord failed: %s", derr.message); - if (dbus_error_has_name(&derr, - "org.bluez.Error.ConnectionAttemptFailed")) - error_connection_attempt_failed(connection, data->msg, - EHOSTDOWN); - else - error_failed(connection, data->msg, derr.message); - dbus_error_free(&derr); - goto failed; - } - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &array, &array_len, - DBUS_TYPE_INVALID)) { - error_failed(connection, data->msg, - "Unable to get args from GetRecordReply"); - goto failed; - } - - record = sdp_extract_pdu(array, &record_len); - if (!record) { - error("Unable to extract service record from reply"); - goto done; - } - - if (record_len != array_len) - debug("warning: array len (%d) != record len (%d)", - array_len, record_len); - - data->records = g_slist_append(data->records, record); - -done: - dbus_message_unref(reply); - - if (data->handles) - get_next_record(data); - else - finish_sdp(data, TRUE); - - return; - -failed: - if (reply) - dbus_message_unref(reply); - finish_sdp(data, FALSE); -} - -static void get_next_record(struct audio_sdp_data *data) -{ - DBusMessage *msg; - DBusPendingCall *pending; - char address[18], *ptr = address; - dbus_uint32_t *handle; - - msg = dbus_message_new_method_call("org.bluez", - data->device->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceRecord"); - if (!msg) { - error("Unable to allocate new method call"); - error_out_of_memory(connection, data->msg); - finish_sdp(data, FALSE); - return; - } - - handle = data->handles->data; - - data->handles = g_slist_remove(data->handles, data->handles->data); - - ba2str(&data->device->dst, address); - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_UINT32, handle, - DBUS_TYPE_INVALID); - - g_free(handle); + struct audio_sdp_data *data = user_data; + sdp_list_t *seq; + uuid_t uuid; - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { - error("Sending GetRemoteServiceRecord failed"); - error_connection_attempt_failed(connection, data->msg, EIO); + if (err < 0) { + error_connection_attempt_failed(connection, data->msg, -err); finish_sdp(data, FALSE); return; } - dbus_pending_call_set_notify(pending, - (DBusPendingCallNotifyFunction) get_record_reply, - data, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); -} - -static GSList *find_handle(GSList *handles, dbus_uint32_t handle) -{ - while (handles) { - if (*(dbus_uint32_t *) handles->data == handle) - return handles; - handles = handles->next; - } - - return NULL; -} - -static void get_handles_reply(DBusPendingCall *call, - struct audio_sdp_data *data) -{ - DBusMessage *reply; - DBusError derr; - dbus_uint32_t *array = NULL; - int array_len, i; + for (seq = recs; seq; seq = seq->next) { + sdp_record_t *rec = (sdp_record_t *) seq->data; - reply = dbus_pending_call_steal_reply(call); + if (!rec) + break; - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - /* FIXME : forward error message as is */ - error("GetRemoteServiceHandles failed: %s", derr.message); - if (dbus_error_has_name(&derr, - "org.bluez.Error.ConnectionAttemptFailed")) - error_connection_attempt_failed(connection, data->msg, - EHOSTDOWN); - else - error_failed(connection, data->msg, derr.message); - dbus_error_free(&derr); - goto failed; + data->records = g_slist_append(data->records, rec); } - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, - &array, &array_len, - DBUS_TYPE_INVALID)) { - error_failed(connection, data->msg, - "Unable to get args from reply"); - goto failed; - } - - for (i = 0; i < array_len; i++) { - if (!find_handle(data->handles, array[i])) { - dbus_uint32_t *handle = g_new(dbus_uint32_t, 1); - *handle = array[i]; - data->handles = g_slist_append(data->handles, handle); - } - } + sdp_list_free(recs, NULL); data->state++; switch (data->state) { case ADVANCED_AUDIO: - get_handles(ADVANCED_AUDIO_UUID, data); + sdp_uuid16_create(&uuid, ADVANCED_AUDIO_SVCLASS_ID); break; case AV_REMOTE: - get_handles(AVRCP_REMOTE_UUID, data); + sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID); break; default: - if (data->handles) - get_next_record(data); - else - finish_sdp(data, TRUE); + finish_sdp(data, TRUE); + return; } - dbus_message_unref(reply); - - return; - -failed: - dbus_message_unref(reply); - finish_sdp(data, FALSE); + get_records(&uuid, data); } -static DBusHandlerResult get_handles(const char *uuid, - struct audio_sdp_data *data) +static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data) { - DBusPendingCall *pending; - char address[18]; - const char *ptr = address; - DBusMessage *msg; - - msg = dbus_message_new_method_call("org.bluez", - data->device->adapter_path, - "org.bluez.Adapter", - "GetRemoteServiceHandles"); - if (!msg) { - error_failed(connection, data->msg, - "Could not create a new dbus message"); - goto failed; - } - - ba2str(&data->device->dst, address); - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &ptr, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); + struct device *device = data->device; + int err; - if (!dbus_connection_send_with_reply(connection, msg, &pending, -1)) { - error_failed(connection, data->msg, - "Sending GetRemoteServiceHandles failed"); - goto failed; - } - - dbus_pending_call_set_notify(pending, - (DBusPendingCallNotifyFunction) get_handles_reply, - data, NULL); - dbus_pending_call_unref(pending); - dbus_message_unref(msg); + err = bt_search_service(&device->src, &device->dst, uuid, + get_records_cb, data, NULL); + if (!err) + return DBUS_HANDLER_RESULT_HANDLED; - return DBUS_HANDLER_RESULT_HANDLED; + if (data->msg) + error_connection_attempt_failed(connection, data->msg, -err); -failed: - if (msg) - dbus_message_unref(msg); finish_sdp(data, FALSE); + return DBUS_HANDLER_RESULT_HANDLED; } @@ -630,6 +455,7 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, void *user_data) { struct audio_sdp_data *sdp_data; + uuid_t uuid; sdp_data = g_new0(struct audio_sdp_data, 1); if (msg) @@ -638,7 +464,9 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, sdp_data->cb = cb; sdp_data->cb_data = user_data; - return get_handles(GENERIC_AUDIO_UUID, sdp_data); + sdp_uuid16_create(&uuid, GENERIC_AUDIO_SVCLASS_ID); + + return get_records(&uuid, sdp_data); } struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) -- cgit From 098cd10838193272c669348c5ffd0410218e1bcc Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Thu, 1 May 2008 01:05:46 +0000 Subject: fixed service authorization --- audio/main.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index b2566c95..47e2c513 100644 --- a/audio/main.c +++ b/audio/main.c @@ -41,6 +41,20 @@ #include "device.h" #include "manager.h" +static const char *uuids[] = { + GENERIC_AUDIO_UUID, + HSP_HS_UUID, + HSP_AG_UUID, + HFP_HS_UUID, + HFP_AG_UUID, + ADVANCED_AUDIO_UUID, + A2DP_SOURCE_UUID, + A2DP_SINK_UUID, + AVRCP_REMOTE_UUID, + AVRCP_TARGET_UUID, + NULL +}; + static GKeyFile *load_config_file(const char *file) { GError *err = NULL; @@ -82,6 +96,8 @@ static int audio_init(void) g_key_file_free(config); + register_uuids("audio", &uuids); + register_external_service(conn, "audio", "Audio service", ""); return 0; -- cgit From 3f8860031e5241fbb145fdd71e1a46c2c28f393e Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Thu, 1 May 2008 01:23:02 +0000 Subject: fixed build warnings --- audio/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 47e2c513..22a5e20a 100644 --- a/audio/main.c +++ b/audio/main.c @@ -35,6 +35,7 @@ #include #include "plugin.h" +#include "dbus-service.h" #include "dbus.h" #include "logging.h" #include "unix.h" @@ -96,7 +97,7 @@ static int audio_init(void) g_key_file_free(config); - register_uuids("audio", &uuids); + register_uuids("audio", uuids); register_external_service(conn, "audio", "Audio service", ""); -- cgit From 9491a544f622e40453265c30f24ce44a61440cc1 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Thu, 1 May 2008 13:52:26 +0000 Subject: Moving authorization code to dbus-service.c --- audio/main.c | 5 ++--- audio/manager.c | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 22a5e20a..eaa04806 100644 --- a/audio/main.c +++ b/audio/main.c @@ -29,14 +29,13 @@ #include #include #include -#include #include -#include + +#include "dbus.h" #include "plugin.h" #include "dbus-service.h" -#include "dbus.h" #include "logging.h" #include "unix.h" #include "device.h" diff --git a/audio/manager.c b/audio/manager.c index 9211ccb7..f33dc963 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -49,7 +49,9 @@ #include #include "dbus-helper.h" -#include "dbus.h" +#include "glib-helper.h" + +#include "dbus-service.h" #include "logging.h" #include "textfile.h" #include "ipc.h" @@ -63,8 +65,6 @@ #include "control.h" #include "manager.h" #include "sdpd.h" -#include "plugin.h" -#include "glib-helper.h" typedef enum { HEADSET = 1 << 0, @@ -1195,7 +1195,7 @@ static void auth_cb(DBusError *derr, void *user_data) error("Access denied: %s", derr->message); if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - if (plugin_cancel_auth(&device->dst) < 0) + if (service_cancel_auth(&device->dst) < 0) manager_cancel_authorize(&device->dst, uuid, NULL); } @@ -1283,7 +1283,7 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (plugin_req_auth(&device->src, &device->dst, uuid, auth_cb, + if (service_req_auth(&device->src, &device->dst, uuid, auth_cb, device) == 0) goto proceed; else if (!manager_authorize(&device->dst, uuid, auth_cb_old, device, -- 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/avdtp.c | 60 ++++++++++++++++++++++++++++++++------------------------- audio/control.c | 55 ++++++++++++++++++++++++++++------------------------ 2 files changed, 64 insertions(+), 51 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ec2a8c12..2e22d8a2 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -40,6 +40,7 @@ #include #include "dbus.h" +#include "dbus-service.h" #include "logging.h" #include "device.h" @@ -2679,34 +2680,23 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep) return 0; } -static void auth_cb(DBusPendingCall *call, void *data) +static void auth_cb(DBusError *derr, void *user_data) { - GIOChannel *io; - struct avdtp *session = data; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; + struct avdtp *session = user_data; struct device *dev; + GIOChannel *io; - 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, ADVANCED_AUDIO_UUID, NULL); } - dbus_error_free(&err); - connection_lost(session, -EACCES); - - dbus_message_unref(reply); - return; } @@ -2730,6 +2720,23 @@ static void auth_cb(DBusPendingCall *call, void *data) g_io_channel_unref(io); } +static void auth_cb_old(DBusPendingCall *call, void *data) +{ + struct avdtp *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); + dbus_set_error_from_message(&err, reply); + auth_cb(&err, data); + dbus_error_free(&err); + + dbus_message_unref(reply); +} + static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) { int srv_sk, cli_sk; @@ -2796,13 +2803,6 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) return TRUE; } - if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb, session, - &session->pending_auth)) { - close(cli_sk); - avdtp_unref(session); - return TRUE; - } - session->mtu = l2o.imtu; session->sock = cli_sk; @@ -2811,6 +2811,14 @@ static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) (GIOFunc) session_cb, session); g_io_channel_unref(io); + if (service_req_auth(&src, &dst, ADVANCED_AUDIO_UUID, auth_cb, session) == 0) + return TRUE; + else if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb_old, + session, &session->pending_auth)) { + close(cli_sk); + avdtp_unref(session); + } + return TRUE; } 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 f85b9560ece47c94ec82466cba9c44da715591d9 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 7 May 2008 18:39:36 +0000 Subject: Make bt_l2cap_connect to take mtu as paramter. --- audio/avdtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 2e22d8a2..2f2e131e 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1590,7 +1590,7 @@ static int l2cap_connect(struct avdtp *session) { int err; - err = bt_l2cap_connect(&session->src, &session->dst, AVDTP_PSM, + err = bt_l2cap_connect(&session->src, &session->dst, AVDTP_PSM, 0, l2cap_connect_cb, session); if (err < 0) { error("Connect failed. %s(%d)", strerror(-err), -err); -- 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/avdtp.c | 4 ++-- audio/control.c | 4 ++-- audio/manager.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 2f2e131e..3c35bfc1 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2852,13 +2852,13 @@ static GIOChannel *avdtp_server_socket(gboolean master) addr.l2_psm = htobs(AVDTP_PSM); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("AVDTP server bind: %s", strerror(errno), errno); + error("AVDTP server bind: %s (%d)", strerror(errno), errno); close(sock); return NULL; } if (listen(sock, 4) < 0) { - error("AVDTP server listen: %s", strerror(errno), errno); + error("AVDTP server listen: %s (%d)", strerror(errno), errno); close(sock); return NULL; } 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; } diff --git a/audio/manager.c b/audio/manager.c index f33dc963..bf612bb3 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1337,13 +1337,13 @@ static GIOChannel *server_socket(uint8_t *channel, gboolean master) addr.rc_channel = channel ? *channel : 0; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s", strerror(errno), errno); + error("server bind: %s (%d)", strerror(errno), errno); close(sock); return NULL; } if (listen(sock, 1) < 0) { - error("server listen: %s", strerror(errno), errno); + error("server listen: %s (%d)", strerror(errno), errno); close(sock); return NULL; } -- cgit From 2af3c3a7ddc43577c067892cdfdc06dc4e63386c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 May 2008 17:24:48 +0000 Subject: Remove service daemon activation handling --- audio/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index eaa04806..f54f74de 100644 --- a/audio/main.c +++ b/audio/main.c @@ -96,15 +96,19 @@ static int audio_init(void) g_key_file_free(config); - register_uuids("audio", uuids); + register_service("audio"); - register_external_service(conn, "audio", "Audio service", ""); + register_uuids("audio", uuids); return 0; } static void audio_exit(void) { + unregister_uuids("audio"); + + unregister_service("audio"); + audio_manager_exit(); unix_exit(); -- cgit From b5514e6c7f0258da455bbde02482fbcdb29d4442 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 8 May 2008 18:37:09 +0000 Subject: Register service and UUIDs in one step --- audio/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index f54f74de..5b4e395b 100644 --- a/audio/main.c +++ b/audio/main.c @@ -96,17 +96,13 @@ static int audio_init(void) g_key_file_free(config); - register_service("audio"); - - register_uuids("audio", uuids); + register_service("audio", uuids); return 0; } static void audio_exit(void) { - unregister_uuids("audio"); - unregister_service("audio"); audio_manager_exit(); -- 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 + audio/unix.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') 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 diff --git a/audio/unix.c b/audio/unix.c index bbb58d72..05ac448a 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -40,7 +40,6 @@ #include #include "logging.h" -#include "dbus.h" #include "ipc.h" #include "device.h" #include "manager.h" @@ -49,6 +48,7 @@ #include "headset.h" #include "sink.h" #include "unix.h" +#include "glib-helper.h" typedef enum { TYPE_NONE, -- 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/Makefile.am | 4 ++-- audio/avdtp.c | 6 +++--- audio/control.c | 7 +++---- audio/device.c | 7 +++---- audio/headset.c | 2 -- audio/main.c | 3 +-- audio/sink.c | 5 ++--- 7 files changed, 14 insertions(+), 20 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 841617ea..21d48860 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -14,7 +14,7 @@ libaudio_la_SOURCES = main.c \ libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc LDADD = $(top_builddir)/common/libhelper.a \ - @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ + @GDBUS_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ if ALSA alsadir = $(libdir)/alsa-lib @@ -52,7 +52,7 @@ libgstbluetooth_la_CFLAGS = @GSTREAMER_CFLAGS@ @SBC_CFLAGS@ endif endif -AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ +AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@ INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/hcid -I$(top_srcdir)/sdpd diff --git a/audio/avdtp.c b/audio/avdtp.c index 3c35bfc1..300898e2 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -34,12 +34,12 @@ #include #include -#include - #include #include -#include "dbus.h" +#include +#include + #include "dbus-service.h" #include "logging.h" 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" diff --git a/audio/device.c b/audio/device.c index 0debe220..c385c7ca 100644 --- a/audio/device.c +++ b/audio/device.c @@ -32,16 +32,15 @@ #include #include -#include -#include - #include #include #include #include #include -#include "dbus.h" +#include +#include + #include "dbus-helper.h" #include "logging.h" #include "textfile.h" diff --git a/audio/headset.c b/audio/headset.c index 859a87ba..2d635110 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -47,10 +47,8 @@ #include #include - #include -#include "dbus.h" #include "dbus-helper.h" #include "logging.h" #include "device.h" diff --git a/audio/main.c b/audio/main.c index 5b4e395b..ef606223 100644 --- a/audio/main.c +++ b/audio/main.c @@ -31,8 +31,7 @@ #include #include - -#include "dbus.h" +#include #include "plugin.h" #include "dbus-service.h" diff --git a/audio/sink.c b/audio/sink.c index b44f8de6..b755759d 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -29,12 +29,11 @@ #include #include +#include + #include #include -#include - -#include "dbus.h" #include "dbus-helper.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 +- audio/device.c | 2 +- audio/headset.c | 2 +- audio/manager.c | 3 +-- audio/sink.c | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) (limited to 'audio') 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" diff --git a/audio/device.c b/audio/device.c index c385c7ca..b240bdae 100644 --- a/audio/device.c +++ b/audio/device.c @@ -40,8 +40,8 @@ #include #include +#include -#include "dbus-helper.h" #include "logging.h" #include "textfile.h" diff --git a/audio/headset.c b/audio/headset.c index 2d635110..247d02d4 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -48,8 +48,8 @@ #include #include +#include -#include "dbus-helper.h" #include "logging.h" #include "device.h" #include "manager.h" diff --git a/audio/manager.c b/audio/manager.c index bf612bb3..15136b71 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -45,10 +45,9 @@ #include #include - #include +#include -#include "dbus-helper.h" #include "glib-helper.h" #include "dbus-service.h" diff --git a/audio/sink.c b/audio/sink.c index b755759d..af51a9c7 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -33,8 +33,8 @@ #include #include +#include -#include "dbus-helper.h" #include "logging.h" #include "avdtp.h" -- cgit From f3c0a1a49b0b505b8543b5b3405bd62126be1a24 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 9 May 2008 09:16:32 +0000 Subject: Use -no-undefined for linking plugins --- audio/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index 21d48860..e968ca6d 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -11,7 +11,8 @@ libaudio_la_SOURCES = main.c \ sink.c sink.h avdtp.c avdtp.h \ a2dp.c a2dp.h control.c control.h -libaudio_la_LDFLAGS = -module -avoid-version -export-symbols-regex bluetooth_plugin_desc +libaudio_la_LDFLAGS = -module -avoid-version -no-undefined \ + -export-symbols-regex bluetooth_plugin_desc LDADD = $(top_builddir)/common/libhelper.a \ @GDBUS_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @BLUEZ_LIBS@ -- 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') 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/avdtp.c | 3 ++- audio/control.c | 3 ++- audio/device.c | 5 +++-- audio/device.h | 2 +- audio/headset.c | 7 ++++--- audio/manager.c | 6 +++--- audio/manager.h | 4 ++-- 7 files changed, 17 insertions(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 300898e2..7290578b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1508,7 +1508,8 @@ failed: return FALSE; } -static void l2cap_connect_cb(GIOChannel *chan, int err, gpointer user_data) +static void l2cap_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer user_data) { struct avdtp *session = user_data; struct l2cap_options l2o; 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; diff --git a/audio/device.c b/audio/device.c index b240bdae..120d45f3 100644 --- a/audio/device.c +++ b/audio/device.c @@ -72,7 +72,8 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, return send_message_and_unref(conn, reply); } -static char *get_dev_name(DBusConnection *conn, bdaddr_t *src, bdaddr_t *bda) +static char *get_dev_name(DBusConnection *conn, const bdaddr_t *src, + const bdaddr_t *bda) { char address[18], filename[PATH_MAX + 1]; @@ -191,7 +192,7 @@ static void device_unregister(DBusConnection *conn, void *data) } struct device *device_register(DBusConnection *conn, - const char *path, bdaddr_t *bda) + const char *path, const bdaddr_t *bda) { struct device *dev; bdaddr_t src; diff --git a/audio/device.h b/audio/device.h index c5907075..44515bf4 100644 --- a/audio/device.h +++ b/audio/device.h @@ -70,7 +70,7 @@ struct device { }; struct device *device_register(DBusConnection *conn, - const char *path, bdaddr_t *bda); + const char *path, const bdaddr_t *bda); int device_store(struct device *device, gboolean is_default); diff --git a/audio/headset.c b/audio/headset.c index 247d02d4..40a8a0d3 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -288,8 +288,8 @@ static unsigned int connect_cb_new(struct headset *hs, return cb->id; } -static void sco_connect_cb(GIOChannel *chan, int err, - gpointer user_data) +static void sco_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer user_data) { int sk; struct device *dev = user_data; @@ -675,7 +675,8 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, return FALSE; } -static void rfcomm_connect_cb(GIOChannel *chan, int err, gpointer user_data) +static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer user_data) { struct device *dev = user_data; struct headset *hs = dev->headset; diff --git a/audio/manager.c b/audio/manager.c index 15136b71..f09125f9 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -122,7 +122,7 @@ static struct enabled_interfaces enabled = { static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data); -static struct device *create_device(bdaddr_t *bda) +static struct device *create_device(const bdaddr_t *bda) { static int device_id = 0; char path[128]; @@ -468,7 +468,7 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, return get_records(&uuid, sdp_data); } -struct device *manager_device_connected(bdaddr_t *bda, const char *uuid) +struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid) { struct device *device; const char *path; @@ -1726,7 +1726,7 @@ gboolean manager_authorize(bdaddr_t *dba, const char *uuid, return TRUE; } -struct device *manager_find_device(bdaddr_t *bda, const char *interface, +struct device *manager_find_device(const bdaddr_t *bda, const char *interface, gboolean connected) { GSList *l; diff --git a/audio/manager.h b/audio/manager.h index c8f826ee..acc9c8e2 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -41,10 +41,10 @@ void audio_manager_exit(void); gboolean server_is_enabled(uint16_t svc); -struct device *manager_find_device(bdaddr_t *bda, const char *interface, +struct device *manager_find_device(const bdaddr_t *bda, const char *interface, gboolean connected); -struct device *manager_device_connected(bdaddr_t *bda, const char *uuid); +struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid); gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data); -- 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/avdtp.c | 17 +++++++++-------- audio/control.c | 17 +++++++++-------- audio/main.c | 3 ++- 3 files changed, 20 insertions(+), 17 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 7290578b..c74c99e0 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2940,14 +2940,15 @@ int avdtp_init(GKeyFile *config) if (avdtp_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; + } avdtp_server = avdtp_server_socket(master); if (!avdtp_server) 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); diff --git a/audio/main.c b/audio/main.c index ef606223..6419da9a 100644 --- a/audio/main.c +++ b/audio/main.c @@ -93,7 +93,8 @@ static int audio_init(void) return -EIO; } - g_key_file_free(config); + if (config) + g_key_file_free(config); register_service("audio", uuids); -- cgit From 165b6e46b5524e2ed623925fa45d80d11032e716 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 May 2008 07:42:17 +0000 Subject: Fix return value for snd_pcm_hw_params() calls --- audio/pcm_bluetooth.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c index 13550663..0dec0a12 100644 --- a/audio/pcm_bluetooth.c +++ b/audio/pcm_bluetooth.c @@ -421,7 +421,11 @@ static int bluetooth_prepare(snd_pcm_ioplug_t *io) } /* wake up any client polling at us */ - return write(data->pipefd[1], &c, 1); + err = write(data->pipefd[1], &c, 1); + if (err < 0) + return err; + + return 0; } static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, -- cgit From c30643b049e0d6215f49c77e38092832b5b74179 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 15 May 2008 20:37:45 +0000 Subject: Make audio service to use of bt_*_listen helpers. --- audio/avdtp.c | 124 +++++++++++---------------------------------- audio/headset.c | 4 +- audio/headset.h | 2 +- audio/manager.c | 153 +++++++++++++++----------------------------------------- 4 files changed, 73 insertions(+), 210 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index c74c99e0..761cb2f5 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2738,140 +2738,79 @@ static void auth_cb_old(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean avdtp_server_cb(GIOChannel *chan, GIOCondition cond, void *data) +static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer data) { - int srv_sk, cli_sk; + int sk; socklen_t size; - struct sockaddr_l2 addr; struct l2cap_options l2o; - bdaddr_t src, dst; struct avdtp *session; - GIOChannel *io; char address[18]; - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on AVDTP 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("AVDTP accept: %s (%d)", strerror(errno), errno); - return TRUE; + if (err < 0) { + error("accept: %s (%d)", strerror(-err), -err); + return; } - bacpy(&dst, &addr.l2_bdaddr); + sk = g_io_channel_unix_get_fd(chan); - ba2str(&dst, address); + ba2str(dst, address); debug("AVDTP: 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) { + if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &size) < 0) { error("getsockopt(L2CAP_OPTIONS): %s (%d)", strerror(errno), errno); - close(cli_sk); - return TRUE; + goto drop; } - session = avdtp_get_internal(&src, &dst); + session = avdtp_get_internal((bdaddr_t *) src, (bdaddr_t *) dst); if (session->pending_open && session->pending_open->open_acp) { - handle_transport_connect(session, cli_sk, l2o.imtu, l2o.omtu); - return TRUE; + handle_transport_connect(session, sk, l2o.imtu, l2o.omtu); + return; } if (session->sock >= 0) { error("Refusing unexpected connect from %s", address); - close(cli_sk); - return TRUE; + goto drop; } session->mtu = l2o.imtu; - session->sock = cli_sk; + session->sock = sk; - io = g_io_channel_unix_new(session->sock); - session->io = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + session->io = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session); - g_io_channel_unref(io); + g_io_channel_unref(chan); - if (service_req_auth(&src, &dst, ADVANCED_AUDIO_UUID, auth_cb, session) == 0) - return TRUE; - else if (!manager_authorize(&dst, ADVANCED_AUDIO_UUID, auth_cb_old, + if (service_req_auth((bdaddr_t *) src, (bdaddr_t *) dst, ADVANCED_AUDIO_UUID, + auth_cb, session) == 0) + return; + else if (!manager_authorize((bdaddr_t *) dst, ADVANCED_AUDIO_UUID, auth_cb_old, session, &session->pending_auth)) { - close(cli_sk); avdtp_unref(session); + goto drop; } - return TRUE; + return; + +drop: + g_io_channel_close(chan); + g_io_channel_unref(chan); } static GIOChannel *avdtp_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("AVDTP server socket: %s (%d)", strerror(errno), errno); - return NULL; - } + int lm; lm = L2CAP_LM_SECURE; if (master) lm |= L2CAP_LM_MASTER; - if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) { - error("AVDTP 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(AVDTP_PSM); - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("AVDTP server bind: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 4) < 0) { - error("AVDTP 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; + return bt_l2cap_listen(BDADDR_ANY, AVDTP_PSM, 0, lm, avdtp_server_cb, + NULL); } const char *avdtp_strerror(struct avdtp_error *err) @@ -2954,9 +2893,6 @@ int avdtp_init(GKeyFile *config) if (!avdtp_server) return -1; - g_io_add_watch(avdtp_server, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) avdtp_server_cb, NULL); - return 0; } diff --git a/audio/headset.c b/audio/headset.c index 40a8a0d3..6198ace7 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -1732,11 +1732,11 @@ void set_hfp_active(struct device *dev, gboolean active) hs->hfp_active = active; } -int headset_connect_rfcomm(struct device *dev, int sock) +int headset_connect_rfcomm(struct device *dev, GIOChannel *io) { struct headset *hs = dev->headset; - hs->tmp_rfcomm = g_io_channel_unix_new(sock); + hs->tmp_rfcomm = io; return hs->tmp_rfcomm ? 0 : -EINVAL; } diff --git a/audio/headset.h b/audio/headset.h index b218b46e..188971a5 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -59,7 +59,7 @@ gboolean get_hfp_active(struct device *dev); void set_hfp_active(struct device *dev, gboolean active); void headset_set_authorized(struct device *dev); -int headset_connect_rfcomm(struct device *dev, int sock); +int headset_connect_rfcomm(struct device *dev, GIOChannel *chan); int headset_close_rfcomm(struct device *dev); headset_state_t headset_get_state(struct device *dev); diff --git a/audio/manager.c b/audio/manager.c index f09125f9..260d1bb3 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1226,32 +1226,16 @@ static void auth_cb_old(DBusPendingCall *call, void *data) dbus_message_unref(reply); } -static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +static void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer data) { - int srv_sk, cli_sk; - struct sockaddr_rc addr; - socklen_t size; const char *uuid; struct device *device; gboolean hfp_active; - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on rfcomm server socket"); - g_io_channel_close(chan); - raise(SIGTERM); - return FALSE; - } - - srv_sk = g_io_channel_unix_get_fd(chan); - - size = sizeof(struct sockaddr_rc); - cli_sk = accept(srv_sk, (struct sockaddr *) &addr, &size); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; + if (err < 0) { + error("accept: %s (%d)", strerror(-err), -err); + return; } if (chan == hsp_ag_server) { @@ -1262,103 +1246,42 @@ static gboolean ag_io_cb(GIOChannel *chan, GIOCondition cond, void *data) uuid = HFP_AG_UUID; } - device = manager_device_connected(&addr.rc_bdaddr, uuid); - if (!device) { - close(cli_sk); - return TRUE; - } + device = manager_device_connected(dst, uuid); + if (!device) + goto drop; if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { debug("Refusing new connection since one already exists"); - close(cli_sk); - return TRUE; + goto drop; } set_hfp_active(device, hfp_active); - if (headset_connect_rfcomm(device, cli_sk) < 0) { + if (headset_connect_rfcomm(device, chan) < 0) { error("Allocating new GIOChannel failed!"); - close(cli_sk); - return TRUE; + goto drop; } if (service_req_auth(&device->src, &device->dst, uuid, auth_cb, device) == 0) - goto proceed; + headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); else if (!manager_authorize(&device->dst, uuid, auth_cb_old, device, NULL)) - goto failed; + headset_close_rfcomm(device); -proceed: - headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); + return; - return TRUE; - -failed: - headset_close_rfcomm(device); - - return TRUE; +drop: + g_io_channel_close(chan); + g_io_channel_unref(chan); + return; } -static gboolean hs_io_cb(GIOChannel *chan, GIOCondition cond, void *data) +static void hs_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, + const bdaddr_t *dst, void *data) { /*Stub*/ - return TRUE; -} - -static GIOChannel *server_socket(uint8_t *channel, gboolean master) -{ - int sock, lm; - struct sockaddr_rc addr; - socklen_t sa_len; - GIOChannel *io; - - sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sock < 0) { - error("server socket: %s (%d)", strerror(errno), errno); - return NULL; - } - - lm = RFCOMM_LM_SECURE; - - if (master) - lm |= RFCOMM_LM_MASTER; - - if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - error("server setsockopt: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, BDADDR_ANY); - addr.rc_channel = channel ? *channel : 0; - - if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("server bind: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - if (listen(sock, 1) < 0) { - error("server listen: %s (%d)", strerror(errno), errno); - close(sock); - return NULL; - } - - sa_len = sizeof(struct sockaddr_rc); - getsockname(sock, (struct sockaddr *) &addr, &sa_len); - *channel = addr.rc_channel; - - io = g_io_channel_unix_new(sock); - if (!io) { - error("Unable to allocate new io channel"); - close(sock); - return NULL; - } - - return io; + return; } static int headset_server_init(DBusConnection *conn, GKeyFile *config) @@ -1367,7 +1290,7 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) sdp_record_t *record; gboolean hfp = TRUE, master = TRUE; GError *err = NULL; - uint32_t features; + uint32_t features, flags; if (!enabled.headset) return 0; @@ -1394,7 +1317,13 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) hfp = tmp; } - hsp_ag_server = server_socket(&chan, master); + flags = RFCOMM_LM_SECURE; + + if (master) + flags |= RFCOMM_LM_MASTER; + + hsp_ag_server = bt_rfcomm_listen(BDADDR_ANY, chan, flags, ag_io_cb, + NULL); if (!hsp_ag_server) return -1; @@ -1413,10 +1342,6 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) } hsp_ag_record_id = record->handle; - g_io_add_watch(hsp_ag_server, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) ag_io_cb, NULL); - features = headset_config_init(config); if (!hfp) @@ -1424,7 +1349,8 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) chan = DEFAULT_HF_AG_CHANNEL; - hfp_ag_server = server_socket(&chan, master); + hfp_ag_server = bt_rfcomm_listen(BDADDR_ANY, chan, flags, ag_io_cb, + NULL); if (!hfp_ag_server) return -1; @@ -1443,10 +1369,6 @@ static int headset_server_init(DBusConnection *conn, GKeyFile *config) } hfp_ag_record_id = record->handle; - g_io_add_watch(hfp_ag_server, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) ag_io_cb, NULL); - return 0; } @@ -1456,6 +1378,7 @@ static int gateway_server_init(DBusConnection *conn, GKeyFile *config) sdp_record_t *record; gboolean master = TRUE; GError *err = NULL; + uint32_t flags; if (!enabled.gateway) return 0; @@ -1473,7 +1396,13 @@ static int gateway_server_init(DBusConnection *conn, GKeyFile *config) master = tmp; } - hsp_hs_server = server_socket(&chan, master); + flags = RFCOMM_LM_SECURE; + + if (master) + flags |= RFCOMM_LM_MASTER; + + hsp_hs_server = bt_rfcomm_listen(BDADDR_ANY, chan, flags, hs_io_cb, + NULL); if (!hsp_hs_server) return -1; @@ -1490,11 +1419,9 @@ static int gateway_server_init(DBusConnection *conn, GKeyFile *config) hsp_hs_server = NULL; return -1; } + hsp_hs_record_id = record->handle; - g_io_add_watch(hsp_hs_server, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - (GIOFunc) hs_io_cb, NULL); return 0; } -- cgit From a82aa1c6502cf4aa1dae32befabf05625bad5b68 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 15 May 2008 22:26:47 +0000 Subject: Remove casts. --- audio/avdtp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 761cb2f5..ef51cdd2 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2765,7 +2765,7 @@ static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, goto drop; } - session = avdtp_get_internal((bdaddr_t *) src, (bdaddr_t *) dst); + session = avdtp_get_internal(src, dst); if (session->pending_open && session->pending_open->open_acp) { handle_transport_connect(session, sk, l2o.imtu, l2o.omtu); @@ -2784,11 +2784,11 @@ static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, (GIOFunc) session_cb, session); g_io_channel_unref(chan); - if (service_req_auth((bdaddr_t *) src, (bdaddr_t *) dst, ADVANCED_AUDIO_UUID, - auth_cb, session) == 0) + if (service_req_auth(src, dst, ADVANCED_AUDIO_UUID, auth_cb, + session) == 0) return; - else if (!manager_authorize((bdaddr_t *) dst, ADVANCED_AUDIO_UUID, auth_cb_old, - session, &session->pending_auth)) { + else if (!manager_authorize(dst, ADVANCED_AUDIO_UUID, auth_cb_old, + session, &session->pending_auth)) { avdtp_unref(session); goto drop; } -- cgit From 15beaa4a782951b90b6a3dd5203fd0ed0141ab7b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 15 May 2008 22:31:30 +0000 Subject: Fix problem with non const bdaddr_t --- audio/avdtp.c | 4 ++-- audio/manager.c | 2 +- audio/manager.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index ef51cdd2..00288f4b 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2130,7 +2130,7 @@ static gboolean avdtp_parse_rej(struct avdtp *session, } } -static struct avdtp *find_session(bdaddr_t *src, bdaddr_t *dst) +static struct avdtp *find_session(const bdaddr_t *src, const bdaddr_t *dst) { GSList *l; @@ -2146,7 +2146,7 @@ static struct avdtp *find_session(bdaddr_t *src, bdaddr_t *dst) return NULL; } -static struct avdtp *avdtp_get_internal(bdaddr_t *src, bdaddr_t *dst) +static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst) { struct avdtp *session; diff --git a/audio/manager.c b/audio/manager.c index 260d1bb3..33a0d501 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1610,7 +1610,7 @@ void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, send_message_and_unref(connection, cancel); } -gboolean manager_authorize(bdaddr_t *dba, const char *uuid, +gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, DBusPendingCallNotifyFunction cb, void *user_data, DBusPendingCall **pending) diff --git a/audio/manager.h b/audio/manager.h index acc9c8e2..b64708b8 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -49,7 +49,7 @@ struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid); gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data); -gboolean manager_authorize(bdaddr_t *dba, const char *uuid, +gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, DBusPendingCallNotifyFunction cb, void *user_data, DBusPendingCall **pending); -- cgit From 86ca72eb302bc00c6c58b56d20ba06f1d7000214 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 16 May 2008 12:49:57 +0000 Subject: Use g_key_file_get_string_list instead of g_key_file_get_string --- audio/manager.c | 78 ++++++++++++++++++++++++++------------------------------- 1 file changed, 36 insertions(+), 42 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 33a0d501..bf2b89ea 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1460,53 +1460,47 @@ static void server_exit(void) int audio_manager_init(DBusConnection *conn, GKeyFile *config) { - char *str; - GError *err = NULL; + char **list; + int i; connection = dbus_connection_ref(conn); - if (config) { - str = g_key_file_get_string(config, "General", "Enable", &err); - - if (err) { - debug("audio.conf: %s", err->message); - g_error_free(err); - err = NULL; - } else { - if (strstr(str, "Headset")) - enabled.headset = TRUE; - if (strstr(str, "Gateway")) - enabled.gateway = TRUE; - if (strstr(str, "Sink")) - enabled.sink = TRUE; - if (strstr(str, "Source")) - enabled.source = TRUE; - if (strstr(str, "Control")) - enabled.control = TRUE; - g_free(str); - } - - str = g_key_file_get_string(config, "General", "Disable", &err); - - if (err) { - debug("audio.conf: %s", err->message); - g_error_free(err); - err = NULL; - } else { - if (strstr(str, "Headset")) - enabled.headset = FALSE; - if (strstr(str, "Gateway")) - enabled.gateway = FALSE; - if (strstr(str, "Sink")) - enabled.sink = FALSE; - if (strstr(str, "Source")) - enabled.source = FALSE; - if (strstr(str, "Control")) - enabled.control = FALSE; - g_free(str); - } + if (!config) + goto proceed; + + list = g_key_file_get_string_list(config, "General", "Enable", + NULL, NULL); + for (i = 0; list && list[i] != NULL; i++) { + if (g_str_equal(list[i], "Headset")) + enabled.headset = TRUE; + else if (g_str_equal(list[i], "Gateway")) + enabled.gateway = TRUE; + else if (g_str_equal(list[i], "Sink")) + enabled.sink = TRUE; + else if (g_str_equal(list[i], "Source")) + enabled.source = TRUE; + else if (g_str_equal(list[i], "Control")) + enabled.control = TRUE; + } + g_strfreev(list); + + list = g_key_file_get_string_list(config, "General", "Disable", + NULL, NULL); + for (i = 0; list && list[i] != NULL; i++) { + if (g_str_equal(list[i], "Headset")) + enabled.headset = FALSE; + else if (g_str_equal(list[i], "Gateway")) + enabled.gateway = FALSE; + else if (g_str_equal(list[i], "Sink")) + enabled.sink = FALSE; + else if (g_str_equal(list[i], "Source")) + enabled.source = FALSE; + else if (g_str_equal(list[i], "Control")) + enabled.control = FALSE; } + g_strfreev(list); +proceed: if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, NULL, manager_unregister)) { error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); -- cgit From 30751fe0c0430e0757c018de4f8e6bceee5e85f7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 20 May 2008 21:54:12 +0000 Subject: Fix issues with missing include for PATH_MAX --- audio/device.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index 120d45f3..6760d4bb 100644 --- a/audio/device.c +++ b/audio/device.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include -- 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/avdtp.c | 2 +- audio/avdtp.h | 2 +- audio/control.c | 392 ++++++++++++++++++++++++-------------------------------- 3 files changed, 167 insertions(+), 229 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index 00288f4b..38fc19c6 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -2186,7 +2186,7 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) return avdtp_ref(session); } -gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst) +gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst) { struct avdtp *session; diff --git a/audio/avdtp.h b/audio/avdtp.h index 4e938b6d..de7e6e3d 100644 --- a/audio/avdtp.h +++ b/audio/avdtp.h @@ -196,7 +196,7 @@ struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst); void avdtp_unref(struct avdtp *session); struct avdtp *avdtp_ref(struct avdtp *session); -gboolean avdtp_is_connected(bdaddr_t *src, bdaddr_t *dst); +gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst); struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int size); 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/a2dp.c | 14 +++---- audio/a2dp.h | 2 +- audio/avdtp.c | 6 +-- audio/control.c | 14 +++---- audio/control.h | 10 ++--- audio/device.c | 28 +++++++------- audio/device.h | 14 +++---- audio/headset.c | 118 ++++++++++++++++++++++++++++---------------------------- audio/headset.h | 42 ++++++++++---------- audio/main.c | 49 +++++++++++++++-------- audio/manager.c | 56 +++++++++++++-------------- audio/manager.h | 6 +-- audio/sink.c | 18 ++++----- audio/sink.h | 10 ++--- audio/unix.c | 28 +++++++------- 15 files changed, 215 insertions(+), 200 deletions(-) (limited to 'audio') diff --git a/audio/a2dp.c b/audio/a2dp.c index c3292fef..3e89d6b1 100644 --- a/audio/a2dp.c +++ b/audio/a2dp.c @@ -131,7 +131,7 @@ static void setup_unref(struct a2dp_setup *setup) setup_free(setup); } -static struct device *a2dp_get_dev(struct avdtp *session) +static struct audio_device *a2dp_get_dev(struct avdtp *session) { bdaddr_t addr; @@ -242,13 +242,13 @@ static struct a2dp_setup *find_setup_by_session(struct avdtp *session) return NULL; } -static struct a2dp_setup *find_setup_by_dev(struct device *dev) +static struct a2dp_setup *find_setup_by_dev(struct audio_device *dev) { GSList *l; for (l = setups; l != NULL; l = l->next) { struct a2dp_setup *setup = l->data; - struct device *setup_dev = a2dp_get_dev(setup->session); + struct audio_device *setup_dev = a2dp_get_dev(setup->session); if (setup_dev == dev) return setup; @@ -289,7 +289,7 @@ static gboolean sbc_setconf_ind(struct avdtp *session, uint8_t *category, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct device *dev; + struct audio_device *dev; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap; struct sbc_codec_cap *sbc_cap; @@ -395,7 +395,7 @@ static gboolean mpeg_setconf_ind(struct avdtp *session, uint8_t *category, void *user_data) { struct a2dp_sep *a2dp_sep = user_data; - struct device *dev; + struct audio_device *dev; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("Sink %p: Set_Configuration_Ind", sep); @@ -473,7 +473,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, { struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; - struct device *dev; + struct audio_device *dev; int ret; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) @@ -1163,7 +1163,7 @@ void a2dp_exit() dbus_connection_unref(connection); } -gboolean a2dp_source_cancel(struct device *dev, unsigned int id) +gboolean a2dp_source_cancel(struct audio_device *dev, unsigned int id) { struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; diff --git a/audio/a2dp.h b/audio/a2dp.h index 1e4ab408..6ec3ebc8 100644 --- a/audio/a2dp.h +++ b/audio/a2dp.h @@ -138,7 +138,7 @@ unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data); unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data); -gboolean a2dp_source_cancel(struct device *dev, unsigned int id); +gboolean a2dp_source_cancel(struct audio_device *dev, unsigned int id); gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session); gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session); diff --git a/audio/avdtp.c b/audio/avdtp.c index 38fc19c6..b0cd98ba 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -735,7 +735,7 @@ static void release_stream(struct avdtp_stream *stream, struct avdtp *session) static void connection_lost(struct avdtp *session, int err) { - struct device *dev; + struct audio_device *dev; dev = manager_find_device(&session->dst, AUDIO_CONTROL_INTERFACE, FALSE); @@ -1550,7 +1550,7 @@ static void l2cap_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, } if (session->state == AVDTP_SESSION_STATE_CONNECTING) { - struct device *dev; + struct audio_device *dev; session->mtu = l2o.imtu; session->buf = g_malloc0(session->mtu); @@ -2684,7 +2684,7 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep) static void auth_cb(DBusError *derr, void *user_data) { struct avdtp *session = user_data; - struct device *dev; + struct audio_device *dev; GIOChannel *io; if (derr && dbus_error_is_set(derr)) { 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; diff --git a/audio/control.h b/audio/control.h index 4f3b7e00..bbb5128c 100644 --- a/audio/control.h +++ b/audio/control.h @@ -27,9 +27,9 @@ int avrcp_init(DBusConnection *conn, GKeyFile *config); void avrcp_exit(void); -gboolean avrcp_connect(struct device *dev); -void avrcp_disconnect(struct device *dev); +gboolean avrcp_connect(struct audio_device *dev); +void avrcp_disconnect(struct audio_device *dev); -struct control *control_init(struct device *dev); -void control_free(struct device *dev); -gboolean control_is_active(struct device *dev); +struct control *control_init(struct audio_device *dev); +void control_free(struct audio_device *dev); +gboolean control_is_active(struct audio_device *dev); diff --git a/audio/device.c b/audio/device.c index 6760d4bb..6b8c5318 100644 --- a/audio/device.c +++ b/audio/device.c @@ -57,7 +57,7 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; DBusMessage *reply; char address[18], *ptr = address; @@ -90,7 +90,7 @@ static char *get_dev_name(DBusConnection *conn, const bdaddr_t *src, static DBusHandlerResult device_get_name(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *dev = data; + struct audio_device *dev = data; DBusMessage *reply; const char *name = dev->name ? dev->name : ""; @@ -107,7 +107,7 @@ static DBusHandlerResult device_get_name(DBusConnection *conn, static DBusHandlerResult device_get_adapter(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; DBusMessage *reply; char address[18], *ptr = address; @@ -128,7 +128,7 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessageIter iter, array_iter; - struct device *device = data; + struct audio_device *device = data; DBusMessage *reply; const char *iface; @@ -162,7 +162,7 @@ static DBusMethodVTable device_methods[] = { { NULL, NULL, NULL, NULL } }; -static void device_free(struct device *dev) +static void device_free(struct audio_device *dev) { if (dev->headset) headset_free(dev); @@ -185,17 +185,17 @@ static void device_free(struct device *dev) static void device_unregister(DBusConnection *conn, void *data) { - struct device *device = data; + struct audio_device *device = data; info("Unregistered device path:%s", device->path); device_free(device); } -struct device *device_register(DBusConnection *conn, +struct audio_device *device_register(DBusConnection *conn, const char *path, const bdaddr_t *bda) { - struct device *dev; + struct audio_device *dev; bdaddr_t src; int dev_id; @@ -207,7 +207,7 @@ struct device *device_register(DBusConnection *conn, if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return NULL; - dev = g_new0(struct device, 1); + dev = g_new0(struct audio_device, 1); /* FIXME just to maintain compatibility */ dev->adapter_path = g_strdup_printf("/org/bluez/hci%d", dev_id); @@ -241,7 +241,7 @@ struct device *device_register(DBusConnection *conn, return dev; } -int device_store(struct device *dev, gboolean is_default) +int device_store(struct audio_device *dev, gboolean is_default) { char value[64]; char filename[PATH_MAX + 1]; @@ -285,7 +285,7 @@ int device_store(struct device *dev, gboolean is_default) return textfile_put(filename, dst_addr, value); } -int device_remove_stored(struct device *dev) +int device_remove_stored(struct audio_device *dev) { char filename[PATH_MAX + 1]; char src_addr[18], dst_addr[18]; @@ -298,7 +298,7 @@ int device_remove_stored(struct device *dev) return textfile_del(filename, dst_addr); } -void device_finish_sdp_transaction(struct device *dev) +void device_finish_sdp_transaction(struct audio_device *dev) { char address[18], *addr_ptr = address; DBusMessage *msg; @@ -391,7 +391,7 @@ static uint8_t hs_to_ipc_state(headset_state_t state) } } -uint8_t device_get_state(struct device *dev) +uint8_t device_get_state(struct audio_device *dev) { avdtp_state_t sink_state; headset_state_t hs_state; @@ -411,7 +411,7 @@ uint8_t device_get_state(struct device *dev) } #endif -gboolean device_is_connected(struct device *dev, const char *interface) +gboolean device_is_connected(struct audio_device *dev, const char *interface) { if (!interface) { if ((dev->sink || dev->source) && diff --git a/audio/device.h b/audio/device.h index 44515bf4..5dc99b9d 100644 --- a/audio/device.h +++ b/audio/device.h @@ -52,7 +52,7 @@ struct sink; struct headset; struct gateway; -struct device { +struct audio_device { DBusConnection *conn; char *adapter_path; char *path; @@ -69,15 +69,15 @@ struct device { struct target *target; }; -struct device *device_register(DBusConnection *conn, +struct audio_device *device_register(DBusConnection *conn, const char *path, const bdaddr_t *bda); -int device_store(struct device *device, gboolean is_default); +int device_store(struct audio_device *device, gboolean is_default); -int device_remove_stored(struct device *dev); +int device_remove_stored(struct audio_device *dev); -void device_finish_sdp_transaction(struct device *device); +void device_finish_sdp_transaction(struct audio_device *device); -uint8_t device_get_state(struct device *dev); +uint8_t device_get_state(struct audio_device *dev); -gboolean device_is_connected(struct device *dev, const char *interface); +gboolean device_is_connected(struct audio_device *dev, const char *interface); diff --git a/audio/headset.c b/audio/headset.c index 6198ace7..69b89a77 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -142,12 +142,12 @@ struct headset { struct event { const char *cmd; - int (*callback) (struct device *device, const char *buf); + int (*callback) (struct audio_device *device, const char *buf); }; -static int rfcomm_connect(struct device *device, headset_stream_cb_t cb, +static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id); -static int get_records(struct device *device, headset_stream_cb_t cb, +static int get_records(struct audio_device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id); static int headset_send(struct headset *hs, char *format, ...) @@ -183,7 +183,7 @@ static int headset_send(struct headset *hs, char *format, ...) return 0; } -static int supported_features(struct device *device, const char *buf) +static int supported_features(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; int err; @@ -199,7 +199,7 @@ static int supported_features(struct device *device, const char *buf) return headset_send(hs, "\r\nOK\r\n"); } -static int report_indicators(struct device *device, const char *buf) +static int report_indicators(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; int err; @@ -216,7 +216,7 @@ static int report_indicators(struct device *device, const char *buf) return headset_send(hs, "\r\nOK\r\n"); } -static void pending_connect_complete(struct connect_cb *cb, struct device *dev) +static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev) { struct headset *hs = dev->headset; @@ -226,7 +226,7 @@ static void pending_connect_complete(struct connect_cb *cb, struct device *dev) cb->cb(dev, cb->cb_data); } -static void pending_connect_finalize(struct device *dev) +static void pending_connect_finalize(struct audio_device *dev) { struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; @@ -292,7 +292,7 @@ static void sco_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer user_data) { int sk; - struct device *dev = user_data; + struct audio_device *dev = user_data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; @@ -331,7 +331,7 @@ static void sco_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, headset_set_state(dev, HEADSET_STATE_PLAYING); } -static int sco_connect(struct device *dev, headset_stream_cb_t cb, +static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { struct headset *hs = dev->headset; @@ -360,7 +360,7 @@ static int sco_connect(struct device *dev, headset_stream_cb_t cb, return 0; } -static void hfp_slc_complete(struct device *dev) +static void hfp_slc_complete(struct audio_device *dev) { struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; @@ -387,7 +387,7 @@ static void hfp_slc_complete(struct device *dev) pending_connect_finalize(dev); } -static int event_reporting(struct device *dev, const char *buf) +static int event_reporting(struct audio_device *dev, const char *buf) { struct headset *hs = dev->headset; int ret; @@ -407,7 +407,7 @@ static int event_reporting(struct device *dev, const char *buf) return 0; } -static int call_hold(struct device *dev, const char *buf) +static int call_hold(struct audio_device *dev, const char *buf) { struct headset *hs = dev->headset; int err; @@ -428,7 +428,7 @@ static int call_hold(struct device *dev, const char *buf) return 0; } -static int answer_call(struct device *device, const char *buf) +static int answer_call(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; int err; @@ -463,7 +463,7 @@ static int answer_call(struct device *device, const char *buf) return headset_send(hs, "\r\n+CIEV:3,0\r\n"); } -static int terminate_call(struct device *device, const char *buf) +static int terminate_call(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; int err; @@ -492,7 +492,7 @@ static int terminate_call(struct device *device, const char *buf) return headset_send(hs, "\r\n+CIEV:2,0\r\n"); } -static int cli_notification(struct device *device, const char *buf) +static int cli_notification(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; @@ -504,7 +504,7 @@ static int cli_notification(struct device *device, const char *buf) return headset_send(hs, "\r\nOK\r\n"); } -static int signal_gain_setting(struct device *device, const char *buf) +static int signal_gain_setting(struct audio_device *device, const char *buf) { struct headset *hs = device->headset; const char *name; @@ -562,7 +562,7 @@ static struct event event_callbacks[] = { { 0 } }; -static int handle_event(struct device *device, const char *buf) +static int handle_event(struct audio_device *device, const char *buf) { struct event *ev; @@ -576,7 +576,7 @@ static int handle_event(struct device *device, const char *buf) return -EINVAL; } -static void close_sco(struct device *device) +static void close_sco(struct audio_device *device) { struct headset *hs = device->headset; @@ -590,7 +590,7 @@ static void close_sco(struct device *device) } static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, - struct device *device) + struct audio_device *device) { struct headset *hs; unsigned char buf[BUF_SIZE]; @@ -659,7 +659,7 @@ failed: } static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, - struct device *device) + struct audio_device *device) { struct headset *hs; @@ -678,7 +678,7 @@ static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer user_data) { - struct device *dev = user_data; + struct audio_device *dev = user_data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; char hs_address[18]; @@ -736,7 +736,7 @@ failed: static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) { - struct device *dev = user_data; + struct audio_device *dev = user_data; struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; int ch = -1; @@ -826,7 +826,7 @@ failed: headset_set_state(dev, HEADSET_STATE_DISCONNECTED); } -static int get_records(struct device *device, headset_stream_cb_t cb, +static int get_records(struct audio_device *device, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { struct headset *hs = device->headset; @@ -851,7 +851,7 @@ static int get_records(struct device *device, headset_stream_cb_t cb, get_record_cb, device, NULL); } -static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb, +static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb, void *user_data, unsigned int *cb_id) { struct headset *hs = dev->headset; @@ -890,7 +890,7 @@ static int rfcomm_connect(struct device *dev, headset_stream_cb_t cb, static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; @@ -910,7 +910,7 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_bool_t playing; @@ -932,7 +932,7 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; char hs_address[18]; @@ -957,7 +957,7 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; DBusMessage *reply; dbus_bool_t connected; @@ -978,7 +978,7 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; int err; @@ -1000,7 +1000,7 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, static gboolean ring_timer_cb(gpointer data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; int err; @@ -1025,7 +1025,7 @@ static gboolean ring_timer_cb(gpointer data) static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; int err; @@ -1069,7 +1069,7 @@ static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; @@ -1107,7 +1107,7 @@ done: static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; int err; @@ -1143,7 +1143,7 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; @@ -1169,7 +1169,7 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; dbus_uint16_t gain; @@ -1195,7 +1195,7 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, DBusMessage *msg, void *data, char type) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; DBusError derr; @@ -1272,7 +1272,7 @@ static DBusHandlerResult hf_setup_call(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; DBusError derr; @@ -1322,7 +1322,7 @@ static DBusHandlerResult hf_identify_call(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; DBusError derr; @@ -1414,7 +1414,7 @@ static void headset_set_channel(struct headset *headset, sdp_record_t *record, error("Unable to get RFCOMM channel from Headset record"); } -void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) +void headset_update(struct audio_device *dev, sdp_record_t *record, uint16_t svc) { struct headset *headset = dev->headset; @@ -1452,7 +1452,7 @@ void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc) headset_set_channel(headset, record, svc); } -struct headset *headset_init(struct device *dev, sdp_record_t *record, +struct headset *headset_init(struct audio_device *dev, sdp_record_t *record, uint16_t svc) { struct headset *hs; @@ -1607,7 +1607,7 @@ uint32_t headset_config_init(GKeyFile *config) return ag_features; } -void headset_free(struct device *dev) +void headset_free(struct audio_device *dev) { struct headset *hs = dev->headset; @@ -1630,13 +1630,13 @@ void headset_free(struct device *dev) dev->headset = NULL; } -static gboolean hs_dc_timeout(struct device *dev) +static gboolean hs_dc_timeout(struct audio_device *dev) { headset_set_state(dev, HEADSET_STATE_DISCONNECTED); return FALSE; } -gboolean headset_cancel_stream(struct device *dev, unsigned int id) +gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id) { struct headset *hs = dev->headset; struct pending_connect *p = hs->pending; @@ -1678,13 +1678,13 @@ gboolean headset_cancel_stream(struct device *dev, unsigned int id) return TRUE; } -static gboolean dummy_connect_complete(struct device *dev) +static gboolean dummy_connect_complete(struct audio_device *dev) { pending_connect_finalize(dev); return FALSE; } -unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, +unsigned int headset_request_stream(struct audio_device *dev, headset_stream_cb_t cb, void *user_data) { struct headset *hs = dev->headset; @@ -1718,21 +1718,21 @@ unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, return id; } -gboolean get_hfp_active(struct device *dev) +gboolean get_hfp_active(struct audio_device *dev) { struct headset *hs = dev->headset; return hs->hfp_active; } -void set_hfp_active(struct device *dev, gboolean active) +void set_hfp_active(struct audio_device *dev, gboolean active) { struct headset *hs = dev->headset; hs->hfp_active = active; } -int headset_connect_rfcomm(struct device *dev, GIOChannel *io) +int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *io) { struct headset *hs = dev->headset; @@ -1741,7 +1741,7 @@ int headset_connect_rfcomm(struct device *dev, GIOChannel *io) return hs->tmp_rfcomm ? 0 : -EINVAL; } -int headset_close_rfcomm(struct device *dev) +int headset_close_rfcomm(struct audio_device *dev) { struct headset *hs = dev->headset; GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm; @@ -1764,7 +1764,7 @@ int headset_close_rfcomm(struct device *dev) return 0; } -void headset_set_authorized(struct device *dev) +void headset_set_authorized(struct audio_device *dev) { struct headset *hs = dev->headset; @@ -1781,7 +1781,7 @@ void headset_set_authorized(struct device *dev) headset_set_state(dev, HEADSET_STATE_CONNECTED); } -void headset_set_state(struct device *dev, headset_state_t state) +void headset_set_state(struct audio_device *dev, headset_state_t state) { struct headset *hs = dev->headset; @@ -1836,21 +1836,21 @@ void headset_set_state(struct device *dev, headset_state_t state) hs->state = state; } -headset_state_t headset_get_state(struct device *dev) +headset_state_t headset_get_state(struct audio_device *dev) { struct headset *hs = dev->headset; return hs->state; } -int headset_get_channel(struct device *dev) +int headset_get_channel(struct audio_device *dev) { struct headset *hs = dev->headset; return hs->rfcomm_ch; } -gboolean headset_is_active(struct device *dev) +gboolean headset_is_active(struct audio_device *dev) { struct headset *hs = dev->headset; @@ -1860,7 +1860,7 @@ gboolean headset_is_active(struct device *dev) return FALSE; } -gboolean headset_lock(struct device *dev, headset_lock_t lock) +gboolean headset_lock(struct audio_device *dev, headset_lock_t lock) { struct headset *hs = dev->headset; @@ -1872,7 +1872,7 @@ gboolean headset_lock(struct device *dev, headset_lock_t lock) return TRUE; } -gboolean headset_unlock(struct device *dev, headset_lock_t lock) +gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock) { struct headset *hs = dev->headset; @@ -1899,17 +1899,17 @@ gboolean headset_unlock(struct device *dev, headset_lock_t lock) return TRUE; } -gboolean headset_suspend(struct device *dev, void *data) +gboolean headset_suspend(struct audio_device *dev, void *data) { return TRUE; } -gboolean headset_play(struct device *dev, void *data) +gboolean headset_play(struct audio_device *dev, void *data) { return TRUE; } -int headset_get_sco_fd(struct device *dev) +int headset_get_sco_fd(struct audio_device *dev) { struct headset *hs = dev->headset; diff --git a/audio/headset.h b/audio/headset.h index 188971a5..b61a1015 100644 --- a/audio/headset.h +++ b/audio/headset.h @@ -40,38 +40,38 @@ typedef enum { HEADSET_LOCK_WRITE = 1 << 1, } headset_lock_t; -typedef void (*headset_stream_cb_t) (struct device *dev, void *user_data); +typedef void (*headset_stream_cb_t) (struct audio_device *dev, void *user_data); -struct headset *headset_init(struct device *dev, sdp_record_t *record, +struct headset *headset_init(struct audio_device *dev, sdp_record_t *record, uint16_t svc); -void headset_free(struct device *dev); +void headset_free(struct audio_device *dev); uint32_t headset_config_init(GKeyFile *config); -void headset_update(struct device *dev, sdp_record_t *record, uint16_t svc); +void headset_update(struct audio_device *dev, sdp_record_t *record, uint16_t svc); -unsigned int headset_request_stream(struct device *dev, headset_stream_cb_t cb, - void *user_data); -gboolean headset_cancel_stream(struct device *dev, unsigned int id); +unsigned int headset_request_stream(struct audio_device *dev, + headset_stream_cb_t cb, void *user_data); +gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id); -gboolean get_hfp_active(struct device *dev); -void set_hfp_active(struct device *dev, gboolean active); +gboolean get_hfp_active(struct audio_device *dev); +void set_hfp_active(struct audio_device *dev, gboolean active); -void headset_set_authorized(struct device *dev); -int headset_connect_rfcomm(struct device *dev, GIOChannel *chan); -int headset_close_rfcomm(struct device *dev); +void headset_set_authorized(struct audio_device *dev); +int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan); +int headset_close_rfcomm(struct audio_device *dev); -headset_state_t headset_get_state(struct device *dev); -void headset_set_state(struct device *dev, headset_state_t state); +headset_state_t headset_get_state(struct audio_device *dev); +void headset_set_state(struct audio_device *dev, headset_state_t state); -int headset_get_channel(struct device *dev); +int headset_get_channel(struct audio_device *dev); -int headset_get_sco_fd(struct device *dev); +int headset_get_sco_fd(struct audio_device *dev); -gboolean headset_is_active(struct device *dev); +gboolean headset_is_active(struct audio_device *dev); -gboolean headset_lock(struct device *dev, headset_lock_t lock); -gboolean headset_unlock(struct device *dev, headset_lock_t lock); -gboolean headset_suspend(struct device *dev, void *data); -gboolean headset_play(struct device *dev, void *data); +gboolean headset_lock(struct audio_device *dev, headset_lock_t lock); +gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock); +gboolean headset_suspend(struct audio_device *dev, void *data); +gboolean headset_play(struct audio_device *dev, void *data); diff --git a/audio/main.c b/audio/main.c index 6419da9a..2b9a1e95 100644 --- a/audio/main.c +++ b/audio/main.c @@ -34,24 +34,41 @@ #include #include "plugin.h" -#include "dbus-service.h" +#include "../hcid/device.h" #include "logging.h" #include "unix.h" #include "device.h" #include "manager.h" -static const char *uuids[] = { - GENERIC_AUDIO_UUID, - HSP_HS_UUID, - HSP_AG_UUID, - HFP_HS_UUID, - HFP_AG_UUID, - ADVANCED_AUDIO_UUID, - A2DP_SOURCE_UUID, - A2DP_SINK_UUID, - AVRCP_REMOTE_UUID, - AVRCP_TARGET_UUID, - NULL +static DBusConnection *conn; + +static int audio_probe(const char *path) +{ + debug("path %s", path); + + return 0; +} + +static void audio_remove(const char *path) +{ + debug("path %s", path); +} + +static struct btd_device_driver audio_driver = { + .name = "audio", + .uuids = BTD_UUIDS( + GENERIC_AUDIO_UUID, + HSP_HS_UUID, + HSP_AG_UUID, + HFP_HS_UUID, + HFP_AG_UUID, + ADVANCED_AUDIO_UUID, + A2DP_SOURCE_UUID, + A2DP_SINK_UUID, + AVRCP_REMOTE_UUID, + AVRCP_TARGET_UUID), + .probe = audio_probe, + .remove = audio_remove, }; static GKeyFile *load_config_file(const char *file) @@ -71,8 +88,6 @@ static GKeyFile *load_config_file(const char *file) return keyfile; } -static DBusConnection *conn; - static int audio_init(void) { GKeyFile *config; @@ -96,14 +111,14 @@ static int audio_init(void) if (config) g_key_file_free(config); - register_service("audio", uuids); + btd_register_device_driver(&audio_driver); return 0; } static void audio_exit(void) { - unregister_service("audio"); + btd_unregister_device_driver(&audio_driver); audio_manager_exit(); diff --git a/audio/manager.c b/audio/manager.c index bf2b89ea..f6d26abd 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -83,7 +83,7 @@ typedef enum { } audio_sdp_state_t; struct audio_sdp_data { - struct device *device; + struct audio_device *device; DBusMessage *msg; /* Method call or NULL */ @@ -97,8 +97,8 @@ struct audio_sdp_data { static DBusConnection *connection = NULL; -static struct device *default_hs = NULL; -static struct device *default_dev = NULL; +static struct audio_device *default_hs = NULL; +static struct audio_device *default_dev = NULL; static GSList *devices = NULL; @@ -122,7 +122,7 @@ static struct enabled_interfaces enabled = { static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data); -static struct device *create_device(const bdaddr_t *bda) +static struct audio_device *create_device(const bdaddr_t *bda) { static int device_id = 0; char path[128]; @@ -133,12 +133,12 @@ static struct device *create_device(const bdaddr_t *bda) return device_register(connection, path, bda); } -static void destroy_device(struct device *device) +static void destroy_device(struct audio_device *device) { dbus_connection_destroy_object_path(connection, device->path); } -static void remove_device(struct device *device) +static void remove_device(struct audio_device *device) { if (device == default_dev) { debug("Removing default device"); @@ -155,7 +155,7 @@ static void remove_device(struct device *device) destroy_device(device); } -static gboolean add_device(struct device *device, gboolean send_signals) +static gboolean add_device(struct audio_device *device, gboolean send_signals) { if (!send_signals) goto add; @@ -252,7 +252,7 @@ gboolean server_is_enabled(uint16_t svc) return ret; } -static void handle_record(sdp_record_t *record, struct device *device) +static void handle_record(sdp_record_t *record, struct audio_device *device) { gboolean is_default; uint16_t uuid16; @@ -432,7 +432,7 @@ static void get_records_cb(sdp_list_t *recs, int err, gpointer user_data) static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data) { - struct device *device = data->device; + struct audio_device *device = data->device; int err; err = bt_search_service(&device->src, &device->dst, uuid, @@ -449,7 +449,7 @@ static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data) } static DBusHandlerResult resolve_services(DBusMessage *msg, - struct device *device, + struct audio_device *device, create_dev_cb_t cb, void *user_data) { @@ -468,9 +468,9 @@ static DBusHandlerResult resolve_services(DBusMessage *msg, return get_records(&uuid, sdp_data); } -struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid) +struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *uuid) { - struct device *device; + struct audio_device *device; const char *path; gboolean headset = FALSE, created = FALSE; @@ -557,7 +557,7 @@ struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid) gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data) { - struct device *dev; + struct audio_device *dev; dev = create_device(bda); if (!dev) @@ -574,7 +574,7 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, { const char *address, *path; bdaddr_t bda; - struct device *device; + struct audio_device *device; DBusMessage *reply; DBusError derr; @@ -638,7 +638,7 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, DBUS_TYPE_STRING_AS_STRING, &array_iter); for (l = devices; l != NULL; l = l->next) { - struct device *device = l->data; + struct audio_device *device = l->data; if (hs_only && !device->headset) continue; @@ -654,7 +654,7 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, static gint device_path_cmp(gconstpointer a, gconstpointer b) { - const struct device *device = a; + const struct audio_device *device = a; const char *path = b; return strcmp(device->path, path); @@ -668,7 +668,7 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, DBusMessage *reply; GSList *match; const char *path; - struct device *device; + struct audio_device *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -747,7 +747,7 @@ static DBusHandlerResult am_find_by_addr(DBusConnection *conn, const char *address; DBusMessage *reply; DBusError derr; - struct device *device; + struct audio_device *device; bdaddr_t bda; dbus_error_init(&derr); @@ -808,7 +808,7 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, DBusMessage *reply; GSList *match; const char *path; - struct device *device; + struct audio_device *device; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -893,7 +893,7 @@ static DBusSignalVTable manager_signals[] = { static void parse_stored_devices(char *key, char *value, void *data) { bdaddr_t *src = data; - struct device *device; + struct audio_device *device; bdaddr_t dst; if (!key || !value || strcmp(key, "default") == 0) @@ -927,7 +927,7 @@ static void register_devices_stored(const char *adapter) { char filename[PATH_MAX + 1]; struct stat st; - struct device *device; + struct audio_device *device; bdaddr_t default_src; bdaddr_t dst; bdaddr_t src; @@ -1182,7 +1182,7 @@ static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat) static void auth_cb(DBusError *derr, void *user_data) { - struct device *device = user_data; + struct audio_device *device = user_data; const char *uuid; if (get_hfp_active(device)) @@ -1230,7 +1230,7 @@ static void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data) { const char *uuid; - struct device *device; + struct audio_device *device; gboolean hfp_active; if (err < 0) { @@ -1555,17 +1555,17 @@ void audio_manager_exit(void) connection = NULL; } -struct device *manager_default_device(void) +struct audio_device *manager_default_device(void) { return default_dev; } -struct device *manager_get_connected_device(void) +struct audio_device *manager_get_connected_device(void) { GSList *l; for (l = devices; l != NULL; l = g_slist_next(l)) { - struct device *device = l->data; + struct audio_device *device = l->data; if ((device->sink || device->source) && avdtp_is_connected(&device->src, &device->dst)) @@ -1647,7 +1647,7 @@ gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, return TRUE; } -struct device *manager_find_device(const bdaddr_t *bda, const char *interface, +struct audio_device *manager_find_device(const bdaddr_t *bda, const char *interface, gboolean connected) { GSList *l; @@ -1656,7 +1656,7 @@ struct device *manager_find_device(const bdaddr_t *bda, const char *interface, return default_dev; for (l = devices; l != NULL; l = l->next) { - struct device *dev = l->data; + struct audio_device *dev = l->data; if (bacmp(bda, BDADDR_ANY) && bacmp(&dev->dst, bda)) continue; diff --git a/audio/manager.h b/audio/manager.h index b64708b8..6b2b90d8 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -34,17 +34,17 @@ struct enabled_interfaces { gboolean control; }; -typedef void (*create_dev_cb_t) (struct device *dev, void *user_data); +typedef void (*create_dev_cb_t) (struct audio_device *dev, void *user_data); int audio_manager_init(DBusConnection *conn, GKeyFile *config); void audio_manager_exit(void); gboolean server_is_enabled(uint16_t svc); -struct device *manager_find_device(const bdaddr_t *bda, const char *interface, +struct audio_device *manager_find_device(const bdaddr_t *bda, const char *interface, gboolean connected); -struct device *manager_device_connected(const bdaddr_t *bda, const char *uuid); +struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *uuid); gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data); diff --git a/audio/sink.c b/audio/sink.c index af51a9c7..7f33cf13 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -76,7 +76,7 @@ static void stream_state_changed(struct avdtp_stream *stream, struct avdtp_error *err, void *user_data) { - struct device *dev = user_data; + struct audio_device *dev = user_data; struct sink *sink = dev->sink; if (err) @@ -382,7 +382,7 @@ failed: static DBusHandlerResult sink_connect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *dev = data; + struct audio_device *dev = data; struct sink *sink = dev->sink; struct pending_request *pending; @@ -414,7 +414,7 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, static DBusHandlerResult sink_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct sink *sink = device->sink; struct pending_request *pending; int err; @@ -450,7 +450,7 @@ static DBusHandlerResult sink_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { - struct device *device = data; + struct audio_device *device = data; struct sink *sink = device->sink; DBusMessage *reply; dbus_bool_t connected; @@ -484,7 +484,7 @@ static DBusSignalVTable sink_signals[] = { { NULL, NULL } }; -struct sink *sink_init(struct device *dev) +struct sink *sink_init(struct audio_device *dev) { if (!dbus_connection_register_interface(dev->conn, dev->path, AUDIO_SINK_INTERFACE, @@ -495,7 +495,7 @@ struct sink *sink_init(struct device *dev) return g_new0(struct sink, 1); } -void sink_free(struct device *dev) +void sink_free(struct audio_device *dev) { struct sink *sink = dev->sink; @@ -516,7 +516,7 @@ void sink_free(struct device *dev) dev->sink = NULL; } -gboolean sink_is_active(struct device *dev) +gboolean sink_is_active(struct audio_device *dev) { struct sink *sink = dev->sink; @@ -526,14 +526,14 @@ gboolean sink_is_active(struct device *dev) return FALSE; } -avdtp_state_t sink_get_state(struct device *dev) +avdtp_state_t sink_get_state(struct audio_device *dev) { struct sink *sink = dev->sink; return sink->state; } -gboolean sink_new_stream(struct device *dev, struct avdtp *session, +gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session, struct avdtp_stream *stream) { struct sink *sink = dev->sink; diff --git a/audio/sink.h b/audio/sink.h index fb9391df..5e4b6aaa 100644 --- a/audio/sink.h +++ b/audio/sink.h @@ -24,9 +24,9 @@ #define AUDIO_SINK_INTERFACE "org.bluez.audio.Sink" -struct sink *sink_init(struct device *dev); -void sink_free(struct device *dev); -gboolean sink_is_active(struct device *dev); -avdtp_state_t sink_get_state(struct device *dev); -gboolean sink_new_stream(struct device *dev, struct avdtp *session, +struct sink *sink_init(struct audio_device *dev); +void sink_free(struct audio_device *dev); +gboolean sink_is_active(struct audio_device *dev); +avdtp_state_t sink_get_state(struct audio_device *dev); +gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session, struct avdtp_stream *stream); diff --git a/audio/unix.c b/audio/unix.c index 05ac448a..93f5788b 100644 --- a/audio/unix.c +++ b/audio/unix.c @@ -57,7 +57,7 @@ typedef enum { TYPE_SOURCE } service_type_t; -typedef void (*notify_cb_t) (struct device *dev, void *data); +typedef void (*notify_cb_t) (struct audio_device *dev, void *data); struct a2dp_data { struct avdtp *session; @@ -70,7 +70,7 @@ struct headset_data { }; struct unix_client { - struct device *dev; + struct audio_device *dev; GSList *caps; service_type_t type; char *interface; @@ -83,7 +83,7 @@ struct unix_client { int data_fd; /* To be deleted once two phase configuration is fully implemented */ unsigned int req_id; unsigned int cb_id; - gboolean (*cancel) (struct device *dev, unsigned int id); + gboolean (*cancel) (struct audio_device *dev, unsigned int id); }; static GSList *clients = NULL; @@ -173,7 +173,7 @@ static void unix_ipc_error(struct unix_client *client, int type, int err) unix_ipc_sendmsg(client, &rsp_hdr->msg_h); } -static service_type_t select_service(struct device *dev, const char *interface) +static service_type_t select_service(struct audio_device *dev, const char *interface) { if (!interface) { if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) @@ -220,7 +220,7 @@ static void stream_state_changed(struct avdtp_stream *stream, } } -static void headset_discovery_complete(struct device *dev, void *user_data) +static void headset_discovery_complete(struct audio_device *dev, void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -247,7 +247,7 @@ failed: client->dev = NULL; } -static void headset_setup_complete(struct device *dev, void *user_data) +static void headset_setup_complete(struct audio_device *dev, void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -298,7 +298,7 @@ failed: client->dev = NULL; } -static void headset_resume_complete(struct device *dev, void *user_data) +static void headset_resume_complete(struct audio_device *dev, void *user_data) { struct unix_client *client = user_data; char buf[BT_AUDIO_IPC_PACKET_SIZE]; @@ -554,7 +554,7 @@ failed: a2dp->stream = NULL; } -static void start_discovery(struct device *dev, struct unix_client *client) +static void start_discovery(struct audio_device *dev, struct unix_client *client) { struct a2dp_data *a2dp; int err = 0; @@ -596,7 +596,7 @@ failed: unix_ipc_error(client, BT_GETCAPABILITIES_RSP, err ? : EIO); } -static void start_config(struct device *dev, struct unix_client *client) +static void start_config(struct audio_device *dev, struct unix_client *client) { struct a2dp_data *a2dp; unsigned int id; @@ -644,7 +644,7 @@ failed: unix_ipc_error(client, BT_SETCONFIGURATION_RSP, EIO); } -static void start_resume(struct device *dev, struct unix_client *client) +static void start_resume(struct audio_device *dev, struct unix_client *client) { struct a2dp_data *a2dp; unsigned int id; @@ -694,7 +694,7 @@ failed: unix_ipc_error(client, BT_STREAMSTART_RSP, EIO); } -static void start_suspend(struct device *dev, struct unix_client *client) +static void start_suspend(struct audio_device *dev, struct unix_client *client) { struct a2dp_data *a2dp; unsigned int id; @@ -744,7 +744,7 @@ failed: unix_ipc_error(client, BT_STREAMSTOP_RSP, EIO); } -static void create_cb(struct device *dev, void *user_data) +static void create_cb(struct audio_device *dev, void *user_data) { struct unix_client *client = user_data; @@ -757,7 +757,7 @@ static void create_cb(struct device *dev, void *user_data) static void handle_getcapabilities_req(struct unix_client *client, struct bt_getcapabilities_req *req) { - struct device *dev; + struct audio_device *dev; bdaddr_t bdaddr; str2ba(req->device, &bdaddr); @@ -888,7 +888,7 @@ static int handle_a2dp_transport(struct unix_client *client, static void handle_setconfiguration_req(struct unix_client *client, struct bt_setconfiguration_req *req) { - struct device *dev; + struct audio_device *dev; bdaddr_t bdaddr; int err = 0; -- cgit From 99d82283b956d9acb9f7b4f09ec047d91daa7058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 29 May 2008 10:41:44 +0000 Subject: Add currently implemented features of the Control interface to the API doc --- audio/audio-api.txt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/audio-api.txt b/audio/audio-api.txt index 63c0a20d..cba20699 100644 --- a/audio/audio-api.txt +++ b/audio/audio-api.txt @@ -293,10 +293,19 @@ Object path(s) /org/bluez/audio/device* org.bluez.audio.Control interface ================================= -[not yet implemented] - This interface is available for remote devices which implement support for a AVRCP controller. Object path(s) /org/bluez/audio/device* +Methods boolean IsConnected() + + Returns TRUE if AVRCP is connected. + +Signals void Connected() + + Sent when a successful AVRCP connection has been made. + + void Disconnected() + + Sent when the AVRCP connection has been disconnected. -- 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 ++-- audio/device.c | 53 +++++----- audio/headset.c | 303 ++++++++++++++++++++++++++++---------------------------- audio/manager.c | 189 +++++++++++++++-------------------- audio/sink.c | 69 +++++++------ 5 files changed, 302 insertions(+), 334 deletions(-) (limited to 'audio') 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); diff --git a/audio/device.c b/audio/device.c index 6b8c5318..c9608b96 100644 --- a/audio/device.c +++ b/audio/device.c @@ -54,7 +54,7 @@ #include "headset.h" #include "sink.h" -static DBusHandlerResult device_get_address(DBusConnection *conn, +static DBusMessage *device_get_address(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -63,14 +63,14 @@ static DBusHandlerResult device_get_address(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; ba2str(&device->dst, address); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return reply; } static char *get_dev_name(DBusConnection *conn, const bdaddr_t *src, @@ -87,7 +87,7 @@ static char *get_dev_name(DBusConnection *conn, const bdaddr_t *src, return textfile_caseget(filename, address); } -static DBusHandlerResult device_get_name(DBusConnection *conn, +static DBusMessage *device_get_name(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *dev = data; @@ -96,15 +96,15 @@ static DBusHandlerResult device_get_name(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return reply; } -static DBusHandlerResult device_get_adapter(DBusConnection *conn, +static DBusMessage *device_get_adapter(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -113,18 +113,18 @@ static DBusHandlerResult device_get_adapter(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; ba2str(&device->src, address); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ptr, DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return reply; } -static DBusHandlerResult device_get_connected(DBusConnection *conn, +static DBusMessage *device_get_connected(DBusConnection *conn, DBusMessage *msg, void *data) { DBusMessageIter iter, array_iter; @@ -134,7 +134,7 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_iter_init_append(reply, &iter); @@ -151,15 +151,15 @@ static DBusHandlerResult device_get_connected(DBusConnection *conn, dbus_message_iter_close_container(&iter, &array_iter); - return send_message_and_unref(conn, reply); + return reply; } -static DBusMethodVTable device_methods[] = { - { "GetAddress", device_get_address, "", "s" }, - { "GetName", device_get_name, "", "s" }, - { "GetAdapter", device_get_adapter, "", "s" }, - { "GetConnectedInterfaces", device_get_connected, "", "as" }, - { NULL, NULL, NULL, NULL } +static GDBusMethodTable device_methods[] = { + { "GetAddress", "", "s", device_get_address }, + { "GetName", "", "s", device_get_name }, + { "GetAdapter", "", "s", device_get_adapter }, + { "GetConnectedInterfaces", "", "as", device_get_connected }, + { } }; static void device_free(struct audio_device *dev) @@ -183,7 +183,7 @@ static void device_free(struct audio_device *dev) g_free(dev); } -static void device_unregister(DBusConnection *conn, void *data) +static void device_unregister(void *data) { struct audio_device *device = data; @@ -216,18 +216,13 @@ struct audio_device *device_register(DBusConnection *conn, return NULL; } - if (!dbus_connection_create_object_path(conn, path, dev, - device_unregister)) { - error("D-Bus failed to register %s path", path); - device_free(dev); - return NULL; - } - - if (!dbus_connection_register_interface(conn, path, - AUDIO_DEVICE_INTERFACE, device_methods, NULL, NULL)) { + if (!g_dbus_register_interface(conn, path, + AUDIO_DEVICE_INTERFACE, + device_methods, NULL, NULL, + dev, device_unregister)) { error("Failed to register %s interface to %s", AUDIO_DEVICE_INTERFACE, path); - dbus_connection_destroy_object_path(conn, path); + device_free(dev); return NULL; } diff --git a/audio/headset.c b/audio/headset.c index 69b89a77..1cdaf746 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -887,7 +887,7 @@ static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb, return 0; } -static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, +static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -895,19 +895,20 @@ static DBusHandlerResult hs_stop(DBusConnection *conn, DBusMessage *msg, DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; headset_set_state(device, HEADSET_STATE_CONNECTED); - send_message_and_unref(conn, reply); - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, +static DBusMessage *hs_is_playing(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -917,19 +918,17 @@ static DBusHandlerResult hs_is_playing(DBusConnection *conn, DBusMessage *msg, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; playing = (hs->state == HEADSET_STATE_PLAYING); dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, DBUS_TYPE_INVALID); - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, +static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -939,21 +938,21 @@ static DBusHandlerResult hs_disconnect(DBusConnection *conn, DBusMessage *msg, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; if (hs->state == HEADSET_STATE_DISCONNECTED) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); headset_set_state(device, HEADSET_STATE_DISCONNECTED); ba2str(&device->dst, hs_address); info("Disconnected from %s, %s", hs_address, device->path); - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_is_connected(DBusConnection *conn, +static DBusMessage *hs_is_connected(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -963,19 +962,17 @@ static DBusHandlerResult hs_is_connected(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; connected = (device->headset->state >= HEADSET_STATE_CONNECTED); 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 DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, +static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -983,19 +980,24 @@ static DBusHandlerResult hs_connect(DBusConnection *conn, DBusMessage *msg, int err; if (hs->state == HEADSET_STATE_CONNECT_IN_PROGRESS) - return error_in_progress(conn, msg, "Connect in progress"); + return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", + "Connect in Progress"); else if (hs->state > HEADSET_STATE_CONNECT_IN_PROGRESS) - return error_already_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".AlreadyConnected", + "Already Connected"); err = rfcomm_connect(device, NULL, NULL, NULL); if (err < 0) - return error_connection_attempt_failed(conn, msg, -err); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".ConnectAttemptFailed", + "Connect Attempt Failed"); hs->auto_dc = FALSE; hs->pending->msg = dbus_message_ref(msg); - return DBUS_HANDLER_RESULT_HANDLED; + return NULL; } static gboolean ring_timer_cb(gpointer data) @@ -1022,7 +1024,7 @@ static gboolean ring_timer_cb(gpointer data) return TRUE; } -static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, +static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; @@ -1031,11 +1033,13 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, int err; if (hs->state < HEADSET_STATE_CONNECTED) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; if (hs->ring_timer) { debug("IndicateCall received when already indicating"); @@ -1045,7 +1049,8 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, err = headset_send(hs, "\r\nRING\r\n"); if (err < 0) { dbus_message_unref(reply); - return error_failed_errno(conn, msg, -err); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(-err)); } if (hs->cli_active && hs->ph_number) { @@ -1053,32 +1058,34 @@ static DBusHandlerResult hs_ring(DBusConnection *conn, DBusMessage *msg, hs->ph_number, hs->type); if (err < 0) { dbus_message_unref(reply); - return error_failed_errno(conn, msg, -err); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".Failed", "%s", + strerror(-err)); } } hs->ring_timer = g_timeout_add(RING_INTERVAL, ring_timer_cb, device); done: - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_cancel_ringing(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hs_cancel_ringing(DBusConnection *conn, + DBusMessage *msg, + void *data) { struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply = NULL; if (hs->state < HEADSET_STATE_CONNECTED) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; if (!hs->ring_timer) { debug("Got CancelRinging method call but ringing is not in progress"); @@ -1095,17 +1102,17 @@ done: err = headset_send(hs, "\r\n+CIEV:3,0\r\n"); if (err < 0) { dbus_message_unref(reply); - return error_failed_errno(conn, msg, -err); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".Failed", "%s", + strerror(-err)); } } - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, - void *data) +static DBusMessage *hs_play(DBusConnection *conn, DBusMessage *msg, + void *data) { struct audio_device *device = data; struct headset *hs = device->headset; @@ -1114,17 +1121,24 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, if (sco_hci) { error("Refusing Headset.Play() because SCO HCI routing " "is enabled"); - return error_not_available(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", + "Operation not Available"); } switch (hs->state) { case HEADSET_STATE_DISCONNECTED: case HEADSET_STATE_CONNECT_IN_PROGRESS: - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); case HEADSET_STATE_PLAY_IN_PROGRESS: - return error_in_progress(conn, msg, "Play in progress"); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".InProgress", + "Play in Progress"); case HEADSET_STATE_PLAYING: - return error_already_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".AlreadyConnected", + "Device Already Connected"); case HEADSET_STATE_CONNECTED: default: break; @@ -1132,16 +1146,17 @@ static DBusHandlerResult hs_play(DBusConnection *conn, DBusMessage *msg, err = sco_connect(device, NULL, NULL, NULL); if (err < 0) - return error_failed(conn, msg, strerror(-err)); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(-err)); hs->pending->msg = dbus_message_ref(msg); - return DBUS_HANDLER_RESULT_HANDLED; + return NULL; } -static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hs_get_speaker_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) { struct audio_device *device = data; struct headset *hs = device->headset; @@ -1149,25 +1164,24 @@ static DBusHandlerResult hs_get_speaker_gain(DBusConnection *conn, dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->sp_gain < 0) - return error_not_available(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", + "Operation not Available"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; gain = (dbus_uint16_t) hs->sp_gain; dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hs_get_mic_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) { struct audio_device *device = data; struct headset *hs = device->headset; @@ -1175,53 +1189,48 @@ static DBusHandlerResult hs_get_mic_gain(DBusConnection *conn, dbus_uint16_t gain; if (hs->state < HEADSET_STATE_CONNECTED || hs->mic_gain < 0) - return error_not_available(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", + "Operation not Available"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; gain = (dbus_uint16_t) hs->mic_gain; dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_set_gain(DBusConnection *conn, - DBusMessage *msg, - void *data, char type) +static DBusMessage *hs_set_gain(DBusConnection *conn, + DBusMessage *msg, + void *data, char type) { struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; - DBusError derr; dbus_uint16_t gain; int err; if (hs->state < HEADSET_STATE_CONNECTED) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(conn, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, + DBUS_TYPE_INVALID)) + return NULL; if (gain > 15) - return error_invalid_arguments(conn, msg, - "Must be less than or equal to 15"); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".InvalidArgument", + "Must be less than or equal to 15"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; if (hs->state != HEADSET_STATE_PLAYING) goto done; @@ -1229,7 +1238,8 @@ static DBusHandlerResult hs_set_gain(DBusConnection *conn, err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain); if (err < 0) { dbus_message_unref(reply); - return error_failed(conn, msg, "Unable to send to headset"); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(-err)); } done: @@ -1249,55 +1259,50 @@ done: DBUS_TYPE_INVALID); } - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hs_set_speaker_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hs_set_speaker_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) { return hs_set_gain(conn, msg, data, HEADSET_GAIN_SPEAKER); } -static DBusHandlerResult hs_set_mic_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hs_set_mic_gain(DBusConnection *conn, + DBusMessage *msg, + void *data) { return hs_set_gain(conn, msg, data, HEADSET_GAIN_MICROPHONE); } -static DBusHandlerResult hf_setup_call(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *hf_setup_call(DBusConnection *conn, + DBusMessage *msg, + void *data) { struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; - DBusError derr; const char *value; int err; if (!hs->hfp_active) - return error_not_supported(device->conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotSuppported", + "Not Supported"); if (hs->state < HEADSET_STATE_CONNECTED) - return error_not_connected(conn, msg); - - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &value, - DBUS_TYPE_INVALID); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(conn, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID)) + return NULL; reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; if (!strncmp(value, "incoming", 8)) err = headset_send(hs, "\r\n+CIEV:3,1\r\n"); @@ -1310,74 +1315,70 @@ static DBusHandlerResult hf_setup_call(DBusConnection *conn, if (err < 0) { dbus_message_unref(reply); - return error_failed_errno(conn, msg, -err); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(-err)); } - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusHandlerResult hf_identify_call(DBusConnection *conn, +static DBusMessage *hf_identify_call(DBusConnection *conn, DBusMessage *msg, void *data) { struct audio_device *device = data; struct headset *hs = device->headset; DBusMessage *reply; - DBusError derr; const char *number; dbus_int32_t type; if (!hs->hfp_active && !hs->cli_active) - return error_not_supported(device->conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotSuppported", + "Not Supported"); if (hs->state < HEADSET_STATE_CONNECTED) - return error_not_connected(conn, msg); - - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, DBUS_TYPE_STRING, &number, - DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(conn, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, + DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID)) + return NULL; reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; g_free(hs->ph_number); hs->ph_number = g_strdup(number); hs->type = type; - send_message_and_unref(conn, reply); - - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } -static DBusMethodVTable headset_methods[] = { - { "Connect", hs_connect, "", "" }, - { "Disconnect", hs_disconnect, "", "" }, - { "IsConnected", hs_is_connected, "", "b" }, - { "IndicateCall", hs_ring, "", "" }, - { "CancelCall", hs_cancel_ringing, "", "" }, - { "Play", hs_play, "", "" }, - { "Stop", hs_stop, "", "" }, - { "IsPlaying", hs_is_playing, "", "b" }, - { "GetSpeakerGain", hs_get_speaker_gain, "", "q" }, - { "GetMicrophoneGain", hs_get_mic_gain, "", "q" }, - { "SetSpeakerGain", hs_set_speaker_gain, "q", "" }, - { "SetMicrophoneGain", hs_set_mic_gain, "q", "" }, - { "SetupCall", hf_setup_call, "s", "" }, - { "IdentifyCall", hf_identify_call, "si", "" }, +static GDBusMethodTable headset_methods[] = { + { "Connect", "", "", hs_connect, + G_DBUS_METHOD_FLAG_ASYNC }, + { "Disconnect", "", "", hs_disconnect }, + { "IsConnected", "", "b", hs_is_connected }, + { "IndicateCall", "", "", hs_ring }, + { "CancelCall", "", "", hs_cancel_ringing }, + { "Play", "", "", hs_play, + G_DBUS_METHOD_FLAG_ASYNC }, + { "Stop", "", "", hs_stop }, + { "IsPlaying", "", "b", hs_is_playing }, + { "GetSpeakerGain", "", "q", hs_get_speaker_gain }, + { "GetMicrophoneGain", "", "q", hs_get_mic_gain }, + { "SetSpeakerGain", "q", "", hs_set_speaker_gain }, + { "SetMicrophoneGain", "q", "", hs_set_mic_gain }, + { "SetupCall", "s", "", hf_setup_call }, + { "IdentifyCall", "si", "", hf_identify_call }, { NULL, NULL, NULL, NULL } }; -static DBusSignalVTable headset_signals[] = { +static GDBusSignalTable headset_signals[] = { { "Connected", "" }, { "Disconnected", "" }, { "AnswerRequested", "" }, @@ -1486,10 +1487,10 @@ struct headset *headset_init(struct audio_device *dev, sdp_record_t *record, headset_set_channel(hs, record, svc); register_iface: - if (!dbus_connection_register_interface(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - headset_methods, - headset_signals, NULL)) { + if (!g_dbus_register_interface(dev->conn, dev->path, + AUDIO_HEADSET_INTERFACE, + headset_methods, headset_signals, NULL, + dev, NULL)) { g_free(hs); return NULL; } diff --git a/audio/manager.c b/audio/manager.c index f6d26abd..2c3e4257 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -120,7 +120,7 @@ static struct enabled_interfaces enabled = { .control = TRUE, }; -static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data); +static DBusMessage *get_records(uuid_t *uuid, struct audio_sdp_data *data); static struct audio_device *create_device(const bdaddr_t *bda) { @@ -430,7 +430,7 @@ static void get_records_cb(sdp_list_t *recs, int err, gpointer user_data) get_records(&uuid, data); } -static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data) +static DBusMessage *get_records(uuid_t *uuid, struct audio_sdp_data *data) { struct audio_device *device = data->device; int err; @@ -448,10 +448,10 @@ static DBusHandlerResult get_records(uuid_t *uuid, struct audio_sdp_data *data) return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult resolve_services(DBusMessage *msg, - struct audio_device *device, - create_dev_cb_t cb, - void *user_data) +static DBusMessage *resolve_services(DBusMessage *msg, + struct audio_device *device, + create_dev_cb_t cb, + void *user_data) { struct audio_sdp_data *sdp_data; uuid_t uuid; @@ -568,9 +568,9 @@ gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, return TRUE; } -static DBusHandlerResult am_create_device(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *am_create_device(DBusConnection *conn, + DBusMessage *msg, + void *data) { const char *address, *path; bdaddr_t bda; @@ -601,15 +601,15 @@ static DBusHandlerResult am_create_device(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return reply; } -static DBusHandlerResult am_list_devices(DBusConnection *conn, +static DBusMessage *am_list_devices(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -619,7 +619,6 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, GSList *l; gboolean hs_only = FALSE; - dbus_error_init(&derr); if (dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, @@ -630,7 +629,7 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_iter_init_append(reply, &iter); @@ -649,7 +648,7 @@ static DBusHandlerResult am_list_devices(DBusConnection *conn, dbus_message_iter_close_container(&iter, &array_iter); - return send_message_and_unref(connection, reply); + return reply; } static gint device_path_cmp(gconstpointer a, gconstpointer b) @@ -660,36 +659,28 @@ static gint device_path_cmp(gconstpointer a, gconstpointer b) return strcmp(device->path, path); } -static DBusHandlerResult am_remove_device(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *am_remove_device(DBusConnection *conn, + DBusMessage *msg, + void *data) { - DBusError derr; DBusMessage *reply; GSList *match; const char *path; struct audio_device *device; - dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID)) { - error_invalid_arguments(connection, msg, derr.message); - return DBUS_HANDLER_RESULT_HANDLED; - } - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + DBUS_TYPE_INVALID)) + return NULL; match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return error_device_does_not_exist(connection, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; device = match->data; device_remove_stored(device); @@ -737,99 +728,88 @@ static DBusHandlerResult am_remove_device(DBusConnection *conn, DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - return send_message_and_unref(connection, reply); + return reply; } -static DBusHandlerResult am_find_by_addr(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *am_find_by_addr(DBusConnection *conn, + DBusMessage *msg, + void *data) { const char *address; DBusMessage *reply; - DBusError derr; struct audio_device *device; bdaddr_t bda; - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + DBUS_TYPE_INVALID)) + return NULL; str2ba(address, &bda); device = manager_find_device(&bda, NULL, FALSE); if (!device) - return error_device_does_not_exist(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); - return send_message_and_unref(conn, reply); + return reply; } -static DBusHandlerResult am_default_device(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *am_default_device(DBusConnection *conn, + DBusMessage *msg, + void *data) { DBusMessage *reply; if (!default_dev) - return error_device_does_not_exist(connection, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); if (default_dev->headset == NULL && dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, "DefaultHeadset")) - return error_device_does_not_exist(connection, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_dev->path, DBUS_TYPE_INVALID); - return send_message_and_unref(connection, reply); + return reply; } -static DBusHandlerResult am_change_default_device(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *am_change_default_device(DBusConnection *conn, + DBusMessage *msg, + void *data) { - DBusError derr; DBusMessage *reply; GSList *match; const char *path; struct audio_device *device; - dbus_error_init(&derr); - if (!dbus_message_get_args(msg, &derr, + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID)) { - error_invalid_arguments(connection, msg, derr.message); - return DBUS_HANDLER_RESULT_HANDLED; - } - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + DBUS_TYPE_INVALID)) + return NULL; match = g_slist_find_custom(devices, path, device_path_cmp); if (!match) - return error_device_does_not_exist(connection, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; device = match->data; @@ -847,47 +827,40 @@ static DBusHandlerResult am_change_default_device(DBusConnection *conn, DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); else - return error_device_does_not_exist(connection, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExists", + "Device does not exists"); default_dev = device; device_store(device, TRUE); - return send_message_and_unref(connection, reply); + + return reply; } -static DBusMethodVTable manager_methods[] = { - { "CreateDevice", am_create_device, - "s", "s" }, - { "RemoveDevice", am_remove_device, - "s", "" }, - { "ListDevices", am_list_devices, - "", "as" }, - { "DefaultDevice", am_default_device, - "", "s" }, - { "ChangeDefaultDevice", am_change_default_device, - "s", "" }, - { "CreateHeadset", am_create_device, - "s", "s" }, - { "RemoveHeadset", am_remove_device, - "s", "" }, - { "ListHeadsets", am_list_devices, - "", "as" }, - { "FindDeviceByAddress", am_find_by_addr, - "s", "s" }, - { "DefaultHeadset", am_default_device, - "", "s" }, - { "ChangeDefaultHeadset", am_change_default_device, - "s", "" }, - { NULL, NULL, NULL, NULL }, +static GDBusMethodTable manager_methods[] = { + { "CreateDevice", "s", "s", am_create_device, + G_DBUS_METHOD_FLAG_ASYNC }, + { "RemoveDevice", "s", "", am_remove_device }, + { "ListDevices", "", "as", am_list_devices }, + { "DefaultDevice", "", "s", am_default_device }, + { "ChangeDefaultDevice", "s", "", am_change_default_device }, + { "CreateHeadset", "s", "s", am_create_device, + G_DBUS_METHOD_FLAG_ASYNC }, + { "RemoveHeadset", "s", "", am_remove_device }, + { "ListHeadsets", "", "as", am_list_devices }, + { "FindDeviceByAddress", "s", "s", am_find_by_addr }, + { "DefaultHeadset", "", "s", am_default_device }, + { "ChangeDefaultHeadset", "s", "", am_change_default_device }, + { } }; -static DBusSignalVTable manager_signals[] = { +static GDBusSignalTable manager_signals[] = { { "DeviceCreated", "s" }, { "DeviceRemoved", "s" }, { "HeadsetCreated", "s" }, { "HeadsetRemoved", "s" }, { "DefaultDeviceChanged", "s" }, { "DefaultHeadsetChanged", "s" }, - { NULL, NULL } + { } }; static void parse_stored_devices(char *key, char *value, void *data) @@ -992,7 +965,7 @@ static void register_stored(void) closedir(dir); } -static void manager_unregister(DBusConnection *conn, void *data) +static void manager_unregister(void *data) { info("Unregistered manager path"); @@ -1501,12 +1474,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *config) g_strfreev(list); proceed: - if (!dbus_connection_create_object_path(conn, AUDIO_MANAGER_PATH, - NULL, manager_unregister)) { - error("D-Bus failed to register %s path", AUDIO_MANAGER_PATH); - goto failed; - } - if (enabled.headset) { if (headset_server_init(conn, config) < 0) goto failed; @@ -1525,10 +1492,10 @@ proceed: if (enabled.control && avrcp_init(conn, config) < 0) goto failed; - if (!dbus_connection_register_interface(conn, AUDIO_MANAGER_PATH, - AUDIO_MANAGER_INTERFACE, - manager_methods, - manager_signals, NULL)) { + if (!g_dbus_register_interface(conn, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE, + manager_methods, manager_signals, + NULL, NULL, manager_unregister)) { error("Failed to register %s interface to %s", AUDIO_MANAGER_INTERFACE, AUDIO_MANAGER_PATH); goto failed; diff --git a/audio/sink.c b/audio/sink.c index 7f33cf13..9027cd74 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -379,8 +379,8 @@ failed: error_failed(pending->conn, pending->msg, "Stream setup failed"); } -static DBusHandlerResult sink_connect(DBusConnection *conn, - DBusMessage *msg, void *data) +static DBusMessage *sink_connect(DBusConnection *conn, + DBusMessage *msg, void *data) { struct audio_device *dev = data; struct sink *sink = dev->sink; @@ -390,14 +390,17 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, sink->session = avdtp_get(&dev->src, &dev->dst); if (!sink->session) - return error_failed(conn, msg, "Unable to get a session"); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "Unable to get a session"); if (sink->connect || sink->disconnect) - return error_in_progress(conn, msg, "Device connection" - "already in progress"); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(EBUSY)); if (sink->state >= AVDTP_STATE_OPEN) - return error_already_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".AlreadyConnected", + "Device Already Connected"); pending = g_new0(struct pending_request, 1); pending->conn = dbus_connection_ref(conn); @@ -408,11 +411,11 @@ static DBusHandlerResult sink_connect(DBusConnection *conn, debug("stream creation in progress"); - return DBUS_HANDLER_RESULT_HANDLED; + return NULL; } -static DBusHandlerResult sink_disconnect(DBusConnection *conn, - DBusMessage *msg, void *data) +static DBusMessage *sink_disconnect(DBusConnection *conn, + DBusMessage *msg, void *data) { struct audio_device *device = data; struct sink *sink = device->sink; @@ -420,35 +423,39 @@ static DBusHandlerResult sink_disconnect(DBusConnection *conn, int err; if (!sink->session) - return error_not_connected(conn, msg); + return g_dbus_create_error(msg, ERROR_INTERFACE + ".NotConnected", + "Device not Connected"); if (sink->connect || sink->disconnect) - return error_failed(conn, msg, strerror(EBUSY)); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(EBUSY)); if (sink->state < AVDTP_STATE_OPEN) { DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; avdtp_unref(sink->session); sink->session = NULL; - return send_message_and_unref(conn, reply); + return reply; } err = avdtp_close(sink->session, sink->stream); if (err < 0) - return error_failed(conn, msg, strerror(-err)); + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + "%s", strerror(-err)); pending = g_new0(struct pending_request, 1); pending->conn = dbus_connection_ref(conn); pending->msg = dbus_message_ref(msg); sink->disconnect = pending; - return DBUS_HANDLER_RESULT_HANDLED; + return NULL; } -static DBusHandlerResult sink_is_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) +static DBusMessage *sink_is_connected(DBusConnection *conn, + DBusMessage *msg, + void *data) { struct audio_device *device = data; struct sink *sink = device->sink; @@ -457,26 +464,26 @@ static DBusHandlerResult sink_is_connected(DBusConnection *conn, reply = dbus_message_new_method_return(msg); if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return NULL; connected = (sink->state >= AVDTP_STATE_CONFIGURED); 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 sink_methods[] = { - { "Connect", sink_connect, "", "" }, - { "Disconnect", sink_disconnect, "", "" }, - { "IsConnected", sink_is_connected, "", "b" }, +static GDBusMethodTable sink_methods[] = { + { "Connect", "", "", sink_connect, + G_DBUS_METHOD_FLAG_ASYNC }, + { "Disconnect", "", "", sink_disconnect, + G_DBUS_METHOD_FLAG_ASYNC }, + { "IsConnected", "", "b", sink_is_connected }, { NULL, NULL, NULL, NULL } }; -static DBusSignalVTable sink_signals[] = { +static GDBusSignalTable sink_signals[] = { { "Connected", "" }, { "Disconnected", "" }, { "Playing", "" }, @@ -486,10 +493,10 @@ static DBusSignalVTable sink_signals[] = { struct sink *sink_init(struct audio_device *dev) { - if (!dbus_connection_register_interface(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - sink_methods, - sink_signals, NULL)) + if (!g_dbus_register_interface(dev->conn, dev->path, + AUDIO_SINK_INTERFACE, + sink_methods, sink_signals, NULL, + dev, NULL)) return NULL; return g_new0(struct sink, 1); -- cgit From 67bbac85fb123990f461a5eeb2e0154dd6be87df Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 3 Jun 2008 15:10:20 +0000 Subject: Replace destory function with proper unregister calls --- audio/manager.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 2c3e4257..0a8b6478 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -135,7 +135,7 @@ static struct audio_device *create_device(const bdaddr_t *bda) static void destroy_device(struct audio_device *device) { - dbus_connection_destroy_object_path(connection, device->path); + g_dbus_unregister_all_interfaces(connection, device->path); } static void remove_device(struct audio_device *device) @@ -1515,7 +1515,8 @@ void audio_manager_exit(void) { server_exit(); - dbus_connection_destroy_object_path(connection, AUDIO_MANAGER_PATH); + g_dbus_unregister_interface(connection, AUDIO_MANAGER_PATH, + AUDIO_MANAGER_INTERFACE); dbus_connection_unref(connection); -- cgit From 2aab870f593da0b3ec83095e5ded93b900e4b600 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 3 Jun 2008 20:07:34 +0000 Subject: Update autoconf/automake options --- audio/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index e968ca6d..ca9abddd 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -1,5 +1,5 @@ -if AUDIOSERVICE +if AUDIOPLUGIN plugindir = $(libdir)/bluetooth/plugins plugin_LTLIBRARIES = libaudio.la -- cgit From f269e27ce7e39e6484cf02a137dcda7458a7fd85 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 6 Jun 2008 10:20:21 +0000 Subject: Fix the last remains of sending helpers --- audio/device.c | 4 +++- audio/headset.c | 9 ++++++--- audio/manager.c | 7 +++++-- audio/sink.c | 9 ++++++--- 4 files changed, 20 insertions(+), 9 deletions(-) (limited to 'audio') diff --git a/audio/device.c b/audio/device.c index c9608b96..2f5f834f 100644 --- a/audio/device.c +++ b/audio/device.c @@ -311,7 +311,9 @@ void device_finish_sdp_transaction(struct audio_device *dev) dbus_message_append_args(msg, DBUS_TYPE_STRING, &addr_ptr, DBUS_TYPE_INVALID); - send_message_and_unref(dev->conn, msg); + dbus_connection_send(dev->conn, msg, NULL); + + dbus_message_unref(msg); } #if 0 diff --git a/audio/headset.c b/audio/headset.c index 1cdaf746..bb28affb 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -321,7 +321,8 @@ static void sco_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, if (p->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); - send_message_and_unref(dev->conn, reply); + dbus_connection_send(dev->conn, reply, NULL); + dbus_message_unref(reply); } pending_connect_finalize(dev); @@ -374,7 +375,8 @@ static void hfp_slc_complete(struct audio_device *dev) if (p->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); - send_message_and_unref(dev->conn, reply); + dbus_connection_send(dev->conn, reply, NULL); + dbus_message_unref(reply); } if (p->target_state == HEADSET_STATE_CONNECTED) { @@ -717,7 +719,8 @@ static void rfcomm_connect_cb(GIOChannel *chan, int err, const bdaddr_t *src, if (p->msg) { DBusMessage *reply = dbus_message_new_method_return(p->msg); - send_message_and_unref(dev->conn, reply); + dbus_connection_send(dev->conn, reply, NULL); + dbus_message_unref(reply); } pending_connect_finalize(dev); diff --git a/audio/manager.c b/audio/manager.c index 0a8b6478..75c44440 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -370,7 +370,8 @@ update: dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->device->path, DBUS_TYPE_INVALID); - send_message_and_unref(connection, reply); + dbus_connection_send(connection, reply, NULL); + dbus_message_unref(reply); } done: @@ -1569,7 +1570,9 @@ void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); - send_message_and_unref(connection, cancel); + dbus_connection_send(connection, cancel, NULL); + + dbus_message_unref(cancel); } gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, diff --git a/audio/sink.c b/audio/sink.c index 9027cd74..3b5447db 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -96,7 +96,8 @@ static void stream_state_changed(struct avdtp_stream *stream, sink->disconnect = NULL; reply = dbus_message_new_method_return(p->msg); - send_message_and_unref(p->conn, reply); + dbus_connection_send(p->conn, reply, NULL); + dbus_message_unref(reply); pending_request_free(p); } @@ -144,7 +145,8 @@ static gboolean stream_setup_retry(gpointer user_data) DBusMessage *reply; debug("Stream successfully created, after XCASE connect:connect"); reply = dbus_message_new_method_return(pending->msg); - send_message_and_unref(pending->conn, reply); + dbus_connection_send(pending->conn, reply, NULL); + dbus_message_unref(reply); } else { debug("Stream setup failed, after XCASE connect:connect"); error_failed(pending->conn, pending->msg, "Stream setup failed"); @@ -169,7 +171,8 @@ static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, DBusMessage *reply; sink->connect = NULL; reply = dbus_message_new_method_return(pending->msg); - send_message_and_unref(pending->conn, reply); + dbus_connection_send(pending->conn, reply, NULL); + dbus_message_unref(reply); pending_request_free(pending); debug("Stream successfully created"); } else { -- cgit From 5a2610e2ba6c5881b71a931623a56480670fbed8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 6 Jun 2008 13:10:31 +0000 Subject: Fix use of DBUS_HANDLER_RESULT_HANDLED. --- audio/manager.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 75c44440..ec9b203e 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -434,19 +434,22 @@ static void get_records_cb(sdp_list_t *recs, int err, gpointer user_data) static DBusMessage *get_records(uuid_t *uuid, struct audio_sdp_data *data) { struct audio_device *device = data->device; + DBusMessage *reply = NULL; int err; err = bt_search_service(&device->src, &device->dst, uuid, get_records_cb, data, NULL); if (!err) - return DBUS_HANDLER_RESULT_HANDLED; + return NULL; if (data->msg) - error_connection_attempt_failed(connection, data->msg, -err); + reply = g_dbus_create_error(data->msg, + ERROR_INTERFACE ".ConnectionAttemptFailed", + strerror(-err)); finish_sdp(data, FALSE); - return DBUS_HANDLER_RESULT_HANDLED; + return reply; } static DBusMessage *resolve_services(DBusMessage *msg, @@ -577,18 +580,11 @@ static DBusMessage *am_create_device(DBusConnection *conn, bdaddr_t bda; struct audio_device *device; DBusMessage *reply; - DBusError derr; - dbus_error_init(&derr); - dbus_message_get_args(msg, &derr, + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&derr)) { - error_invalid_arguments(connection, msg, derr.message); - dbus_error_free(&derr); - return DBUS_HANDLER_RESULT_HANDLED; - } + DBUS_TYPE_INVALID)) + return NULL; str2ba(address, &bda); -- 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 ++++---- audio/headset.c | 18 +++++++++--------- audio/manager.c | 24 ++++++++++++------------ audio/sink.c | 8 ++++---- 4 files changed, 29 insertions(+), 29 deletions(-) (limited to 'audio') 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); diff --git a/audio/headset.c b/audio/headset.c index bb28affb..0adc1e9d 100644 --- a/audio/headset.c +++ b/audio/headset.c @@ -435,7 +435,7 @@ static int answer_call(struct audio_device *device, const char *buf) struct headset *hs = device->headset; int err; - dbus_connection_emit_signal(device->conn, device->path, + g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "AnswerRequested", DBUS_TYPE_INVALID); @@ -470,7 +470,7 @@ static int terminate_call(struct audio_device *device, const char *buf) struct headset *hs = device->headset; int err; - dbus_connection_emit_signal(device->conn, device->path, + g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, "CallTerminated", DBUS_TYPE_INVALID); @@ -542,7 +542,7 @@ static int signal_gain_setting(struct audio_device *device, const char *buf) return -EINVAL; } - dbus_connection_emit_signal(device->conn, device->path, + g_dbus_emit_signal(device->conn, device->path, AUDIO_HEADSET_INTERFACE, name, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); @@ -1248,14 +1248,14 @@ static DBusMessage *hs_set_gain(DBusConnection *conn, done: if (type == HEADSET_GAIN_SPEAKER) { hs->sp_gain = gain; - dbus_connection_emit_signal(conn, device->path, + g_dbus_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "SpeakerGainChanged", DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID); } else { hs->mic_gain = gain; - dbus_connection_emit_signal(conn, device->path, + g_dbus_emit_signal(conn, device->path, AUDIO_HEADSET_INTERFACE, "MicrophoneGainChanged", DBUS_TYPE_UINT16, &gain, @@ -1796,7 +1796,7 @@ void headset_set_state(struct audio_device *dev, headset_state_t state) case HEADSET_STATE_DISCONNECTED: close_sco(dev); headset_close_rfcomm(dev); - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); @@ -1806,12 +1806,12 @@ void headset_set_state(struct audio_device *dev, headset_state_t state) case HEADSET_STATE_CONNECTED: close_sco(dev); if (hs->state < state) { - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Connected", DBUS_TYPE_INVALID); } else if (hs->state == HEADSET_STATE_PLAYING) { - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Stopped", DBUS_TYPE_INVALID); @@ -1824,7 +1824,7 @@ void headset_set_state(struct audio_device *dev, headset_state_t state) G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) sco_cb, dev); - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_HEADSET_INTERFACE, "Playing", DBUS_TYPE_INVALID); diff --git a/audio/manager.c b/audio/manager.c index ec9b203e..630d6017 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -160,14 +160,14 @@ static gboolean add_device(struct audio_device *device, gboolean send_signals) if (!send_signals) goto add; - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); if (device->headset) - dbus_connection_emit_signal(connection, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", @@ -522,7 +522,7 @@ struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *u path = device->path; if (created) { - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceCreated", DBUS_TYPE_STRING, &path, @@ -531,7 +531,7 @@ struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *u } if (headset) - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetCreated", DBUS_TYPE_STRING, &path, @@ -539,7 +539,7 @@ struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *u if (headset && !default_hs) { default_hs = device; - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", DBUS_TYPE_STRING, &path, @@ -548,7 +548,7 @@ struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *u if (!default_dev) { default_dev = device; - dbus_connection_emit_signal(connection, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(connection, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultDeviceChanged", DBUS_TYPE_STRING, &path, @@ -697,13 +697,13 @@ static DBusMessage *am_remove_device(DBusConnection *conn, param = default_dev ? default_dev->path : ""; - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", DBUS_TYPE_STRING, ¶m, DBUS_TYPE_INVALID); - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultDeviceChanged", DBUS_TYPE_STRING, ¶m, @@ -713,13 +713,13 @@ static DBusMessage *am_remove_device(DBusConnection *conn, device_store(default_dev, TRUE); } - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "HeadsetRemoved", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DeviceRemoved", DBUS_TYPE_STRING, &path, @@ -812,13 +812,13 @@ static DBusMessage *am_change_default_device(DBusConnection *conn, if (!dbus_message_is_method_call(msg, AUDIO_MANAGER_INTERFACE, "ChangeDefaultHeadset")) - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultDeviceChanged", DBUS_TYPE_STRING, &device->path, DBUS_TYPE_INVALID); else if (device->headset) - dbus_connection_emit_signal(conn, AUDIO_MANAGER_PATH, + g_dbus_emit_signal(conn, AUDIO_MANAGER_PATH, AUDIO_MANAGER_INTERFACE, "DefaultHeadsetChanged", DBUS_TYPE_STRING, &device->path, diff --git a/audio/sink.c b/audio/sink.c index 3b5447db..2af74bb3 100644 --- a/audio/sink.c +++ b/audio/sink.c @@ -84,7 +84,7 @@ static void stream_state_changed(struct avdtp_stream *stream, switch (new_state) { case AVDTP_STATE_IDLE: - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Disconnected", DBUS_TYPE_INVALID); @@ -110,18 +110,18 @@ static void stream_state_changed(struct avdtp_stream *stream, break; case AVDTP_STATE_OPEN: if (old_state == AVDTP_STATE_CONFIGURED) - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Connected", DBUS_TYPE_INVALID); else if (old_state == AVDTP_STATE_STREAMING) - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Stopped", DBUS_TYPE_INVALID); break; case AVDTP_STATE_STREAMING: - dbus_connection_emit_signal(dev->conn, dev->path, + g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, "Playing", DBUS_TYPE_INVALID); -- cgit From 8bf636881f152a97727774f4f9ea2504c72dfb23 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2008 20:54:55 +0000 Subject: Use DBG in plugin to show function names --- audio/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 2b9a1e95..06a2a046 100644 --- a/audio/main.c +++ b/audio/main.c @@ -44,14 +44,14 @@ static DBusConnection *conn; static int audio_probe(const char *path) { - debug("path %s", path); + DBG("path %s", path); return 0; } static void audio_remove(const char *path) { - debug("path %s", path); + DBG("path %s", path); } static struct btd_device_driver audio_driver = { -- cgit From 6a87d1b7e5ad1046abfcc2e89cc924b6e1d0db67 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2008 21:13:23 +0000 Subject: Have the audio plugin register headset and sink drivers --- audio/main.c | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 06a2a046..9b780aa0 100644 --- a/audio/main.c +++ b/audio/main.c @@ -42,33 +42,42 @@ static DBusConnection *conn; -static int audio_probe(const char *path) +static int headset_probe(const char *path) { DBG("path %s", path); return 0; } -static void audio_remove(const char *path) +static void headset_remove(const char *path) { DBG("path %s", path); } -static struct btd_device_driver audio_driver = { - .name = "audio", - .uuids = BTD_UUIDS( - GENERIC_AUDIO_UUID, - HSP_HS_UUID, - HSP_AG_UUID, - HFP_HS_UUID, - HFP_AG_UUID, - ADVANCED_AUDIO_UUID, - A2DP_SOURCE_UUID, - A2DP_SINK_UUID, - AVRCP_REMOTE_UUID, - AVRCP_TARGET_UUID), - .probe = audio_probe, - .remove = audio_remove, +static struct btd_device_driver headset_driver = { + .name = "headset", + .uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID), + .probe = headset_probe, + .remove = headset_remove, +}; + +static int a2dp_probe(const char *path) +{ + DBG("path %s", path); + + return 0; +} + +static void a2dp_remove(const char *path) +{ + DBG("path %s", path); +} + +static struct btd_device_driver a2dp_driver = { + .name = "sink", + .uuids = BTD_UUIDS(A2DP_SINK_UUID), + .probe = a2dp_probe, + .remove = a2dp_remove, }; static GKeyFile *load_config_file(const char *file) @@ -111,14 +120,18 @@ static int audio_init(void) if (config) g_key_file_free(config); - btd_register_device_driver(&audio_driver); + btd_register_device_driver(&headset_driver); + + btd_register_device_driver(&a2dp_driver); return 0; } static void audio_exit(void) { - btd_unregister_device_driver(&audio_driver); + btd_unregister_device_driver(&a2dp_driver); + + btd_unregister_device_driver(&headset_driver); audio_manager_exit(); -- cgit From 5243ac4fd278b0176ece84cbcec537a92a9c7290 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2008 22:57:11 +0000 Subject: Update probe/remove callback and implement serial API --- audio/main.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index 9b780aa0..86efce27 100644 --- a/audio/main.c +++ b/audio/main.c @@ -42,16 +42,16 @@ static DBusConnection *conn; -static int headset_probe(const char *path) +static int headset_probe(struct btd_device *device) { - DBG("path %s", path); + DBG("path %s", device->path); return 0; } -static void headset_remove(const char *path) +static void headset_remove(struct btd_device *device) { - DBG("path %s", path); + DBG("path %s", device->path); } static struct btd_device_driver headset_driver = { @@ -61,16 +61,16 @@ static struct btd_device_driver headset_driver = { .remove = headset_remove, }; -static int a2dp_probe(const char *path) +static int a2dp_probe(struct btd_device *device) { - DBG("path %s", path); + DBG("path %s", device->path); return 0; } -static void a2dp_remove(const char *path) +static void a2dp_remove(struct btd_device *device) { - DBG("path %s", path); + DBG("path %s", device->path); } static struct btd_device_driver a2dp_driver = { -- cgit From bbec31284f7e4e960c07bddd9fd1d7ee5c990118 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 16 Jun 2008 19:43:09 +0000 Subject: Remove calls to RequestAuthorization. --- audio/manager.c | 91 ++------------------------------------------------------- 1 file changed, 3 insertions(+), 88 deletions(-) (limited to 'audio') diff --git a/audio/manager.c b/audio/manager.c index 630d6017..242f16bb 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1183,19 +1183,6 @@ static void auth_cb(DBusError *derr, void *user_data) } } -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 void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data) { @@ -1232,13 +1219,12 @@ static void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, goto drop; } - if (service_req_auth(&device->src, &device->dst, uuid, auth_cb, + if (!service_req_auth(&device->src, &device->dst, uuid, auth_cb, device) == 0) - headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); - else if (!manager_authorize(&device->dst, uuid, auth_cb_old, device, - NULL)) headset_close_rfcomm(device); + headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); + return; drop: @@ -1543,77 +1529,6 @@ struct audio_device *manager_get_connected_device(void) return NULL; } -void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, - DBusPendingCall *pending) -{ - DBusMessage *cancel; - char addr[18], *address = addr; - - if (pending) - dbus_pending_call_cancel(pending); - - cancel = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "CancelAuthorizationRequest"); - if (!cancel) { - error("Unable to allocate new method call"); - return; - } - - ba2str(dba, addr); - - dbus_message_append_args(cancel, DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - dbus_connection_send(connection, cancel, NULL); - - dbus_message_unref(cancel); -} - -gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, - DBusPendingCallNotifyFunction cb, - void *user_data, - DBusPendingCall **pending) -{ - DBusMessage *auth; - char address[18], *addr_ptr = address; - DBusPendingCall *p; - - ba2str(dba, address); - - debug("Requesting authorization for device %s, UUID %s", - address, uuid); - - auth = dbus_message_new_method_call("org.bluez", "/org/bluez", - "org.bluez.Database", - "RequestAuthorization"); - if (!auth) { - error("Unable to allocate RequestAuthorization method call"); - return FALSE; - } - - dbus_message_append_args(auth, DBUS_TYPE_STRING, &addr_ptr, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - if (!dbus_connection_send_with_reply(connection, auth, &p, -1)) { - error("Sending of authorization request failed"); - dbus_message_unref(auth); - return FALSE; - } - - dbus_pending_call_set_notify(p, cb, user_data, NULL); - if (pending) - *pending = p; - else - dbus_pending_call_unref(p); - - dbus_message_unref(auth); - - return TRUE; -} - struct audio_device *manager_find_device(const bdaddr_t *bda, const char *interface, gboolean connected) { -- 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/avdtp.c | 34 ++------------------- audio/control.c | 91 +++++++++++++++++---------------------------------------- audio/main.c | 26 +++++++++++++++++ audio/manager.c | 12 ++++---- audio/manager.h | 7 ----- 5 files changed, 62 insertions(+), 108 deletions(-) (limited to 'audio') diff --git a/audio/avdtp.c b/audio/avdtp.c index b0cd98ba..bda21ac5 100644 --- a/audio/avdtp.c +++ b/audio/avdtp.c @@ -1496,13 +1496,6 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, return TRUE; failed: - if (session->pending_auth) { - manager_cancel_authorize(&session->dst, ADVANCED_AUDIO_UUID, - session->pending_auth); - dbus_pending_call_unref(session->pending_auth); - session->pending_auth = NULL; - } - connection_lost(session, -EIO); return FALSE; @@ -2691,10 +2684,7 @@ static void auth_cb(DBusError *derr, void *user_data) 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, - ADVANCED_AUDIO_UUID, - NULL); + service_cancel_auth(&session->src, &session->dst); } connection_lost(session, -EACCES); @@ -2721,23 +2711,6 @@ static void auth_cb(DBusError *derr, void *user_data) g_io_channel_unref(io); } -static void auth_cb_old(DBusPendingCall *call, void *data) -{ - struct avdtp *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); - dbus_set_error_from_message(&err, reply); - auth_cb(&err, data); - dbus_error_free(&err); - - dbus_message_unref(reply); -} - static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, const bdaddr_t *dst, gpointer data) { @@ -2785,10 +2758,7 @@ static void avdtp_server_cb(GIOChannel *chan, int err, const bdaddr_t *src, g_io_channel_unref(chan); if (service_req_auth(src, dst, ADVANCED_AUDIO_UUID, auth_cb, - session) == 0) - return; - else if (!manager_authorize(dst, ADVANCED_AUDIO_UUID, auth_cb_old, - session, &session->pending_auth)) { + session) < 0) { avdtp_unref(session); goto drop; } 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) diff --git a/audio/main.c b/audio/main.c index 86efce27..bbe06e05 100644 --- a/audio/main.c +++ b/audio/main.c @@ -80,6 +80,28 @@ static struct btd_device_driver a2dp_driver = { .remove = a2dp_remove, }; +static int audio_probe(struct btd_device *device) +{ + DBG("path %s", device->path); + + return 0; +} + +static void audio_remove(struct btd_device *device) +{ + DBG("path %s", device->path); +} + +static struct btd_device_driver audio_driver = { + .name = "audio", + .uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID, + ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID, + AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID), + .probe = audio_probe, + .remove = audio_remove, +}; + + static GKeyFile *load_config_file(const char *file) { GError *err = NULL; @@ -124,11 +146,15 @@ static int audio_init(void) btd_register_device_driver(&a2dp_driver); + btd_register_device_driver(&audio_driver); + return 0; } static void audio_exit(void) { + btd_unregister_device_driver(&audio_driver); + btd_unregister_device_driver(&a2dp_driver); btd_unregister_device_driver(&headset_driver); diff --git a/audio/manager.c b/audio/manager.c index 242f16bb..18ef9eb4 100644 --- a/audio/manager.c +++ b/audio/manager.c @@ -1164,9 +1164,7 @@ static void auth_cb(DBusError *derr, void *user_data) error("Access denied: %s", derr->message); if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY)) { debug("Canceling authorization request"); - if (service_cancel_auth(&device->dst) < 0) - manager_cancel_authorize(&device->dst, uuid, - NULL); + service_cancel_auth(&device->src, &device->dst); } headset_set_state(device, HEADSET_STATE_DISCONNECTED); @@ -1219,9 +1217,13 @@ static void ag_io_cb(GIOChannel *chan, int err, const bdaddr_t *src, goto drop; } - if (!service_req_auth(&device->src, &device->dst, uuid, auth_cb, - device) == 0) + err = service_req_auth(&device->src, &device->dst, uuid, auth_cb, + device); + if (err < 0) { + debug("Authorization denied: %s", strerror(-err)); headset_close_rfcomm(device); + return; + } headset_set_state(device, HEADSET_STATE_CONNECT_IN_PROGRESS); diff --git a/audio/manager.h b/audio/manager.h index 6b2b90d8..8f7f6be3 100644 --- a/audio/manager.h +++ b/audio/manager.h @@ -48,10 +48,3 @@ struct audio_device *manager_device_connected(const bdaddr_t *bda, const char *u gboolean manager_create_device(bdaddr_t *bda, create_dev_cb_t cb, void *user_data); - -gboolean manager_authorize(const bdaddr_t *dba, const char *uuid, - DBusPendingCallNotifyFunction cb, - void *user_data, - DBusPendingCall **pending); -void manager_cancel_authorize(bdaddr_t *dba, const char *uuid, - DBusPendingCall *pending); -- cgit From a22a088109b664efdee397affed55b039cb0999b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2008 05:47:13 +0000 Subject: Don't use lib prefix for plugins --- audio/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'audio') diff --git a/audio/Makefile.am b/audio/Makefile.am index ca9abddd..0c6acfab 100644 --- a/audio/Makefile.am +++ b/audio/Makefile.am @@ -2,16 +2,16 @@ if AUDIOPLUGIN plugindir = $(libdir)/bluetooth/plugins -plugin_LTLIBRARIES = libaudio.la +plugin_LTLIBRARIES = audio.la -libaudio_la_SOURCES = main.c \ +audio_la_SOURCES = main.c \ manager.h manager.c headset.h headset.c \ ipc.h ipc.c unix.h unix.c \ device.h device.c gateway.h gateway.c \ sink.c sink.h avdtp.c avdtp.h \ a2dp.c a2dp.h control.c control.h -libaudio_la_LDFLAGS = -module -avoid-version -no-undefined \ +audio_la_LDFLAGS = -module -avoid-version -no-undefined \ -export-symbols-regex bluetooth_plugin_desc LDADD = $(top_builddir)/common/libhelper.a \ -- cgit From c4139033616c53b1f790fe165d1a22fcce292e74 Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Mon, 30 Jun 2008 17:35:47 +0000 Subject: Fixed missing include --- audio/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'audio') diff --git a/audio/main.c b/audio/main.c index bbe06e05..607e3ca9 100644 --- a/audio/main.c +++ b/audio/main.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include -- cgit