]> code.delx.au - pulseaudio/commitdiff
bluetooth: Remove module-bluetooth-proximity
authorJoão Paulo Rechi Vita <jprvita@openbossa.org>
Fri, 16 Aug 2013 12:30:41 +0000 (09:30 -0300)
committerTanu Kaskinen <tanu.kaskinen@linux.intel.com>
Sun, 29 Sep 2013 13:54:36 +0000 (16:54 +0300)
module-bluetooth-proximity has not worked for quite a while, since it
uses pre-BlueZ4 APIs. Nobody complained since then, which is a good
indication that it doesn't have much users. Even the original commit
message refers to it more as a toy than as something of great use: "add
new fun module that automatically mutes your audio devices when you
leave with your bluetooth phone, and unmutes when you come back"

Removing it we completely remove the dependency on libbluetooth.

LICENSE
po/POTFILES.in
src/.gitignore
src/Makefile.am
src/modules/bluetooth/module-bluetooth-proximity.c [deleted file]
src/modules/bluetooth/proximity-helper.c [deleted file]

diff --git a/LICENSE b/LICENSE
index 30833e59e3a624f4220f4f5f39b93df881137ef0..226c4ce40eed7d745ae79eff492c66bf329117aa 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -2,16 +2,16 @@ All PulseAudio source files are licensed under the GNU Lesser General Public
 License. (see file LGPL for details)
 
 However, the server side has optional GPL dependencies.  These include the
-libsamplerate and gdbm (core libraries), LIRC (lirc module), FFTW (equalizer
-module) and bluez (bluetooth proximity helper program) libraries, although
-others may also be included in the future.  If PulseAudio is compiled with these
-optional components, this effectively downgrades the license of the server part
-to GPL (see the file GPL for details), exercising section 3 of the LGPL.  In
-such circumstances, you should treat the client library (libpulse) of PulseAudio
-as being LGPL licensed and the server part (libpulsecore) as being GPL licensed.
-Since the PulseAudio daemon, tests, various utilities/helpers and the modules
-link to libpulsecore and/or the afore mentioned optional GPL dependencies they
-are of course also GPL licensed also in this scenario.
+libsamplerate and gdbm (core libraries), LIRC (lirc module) and FFTW (equalizer
+module), although others may also be included in the future.  If PulseAudio is
+compiled with these optional components, this effectively downgrades the
+license of the server part to GPL (see the file GPL for details), exercising
+section 3 of the LGPL.  In such circumstances, you should treat the client
+library (libpulse) of PulseAudio as being LGPL licensed and the server part
+(libpulsecore) as being GPL licensed.  Since the PulseAudio daemon, tests,
+various utilities/helpers and the modules link to libpulsecore and/or the afore
+mentioned optional GPL dependencies they are of course also GPL licensed also
+in this scenario.
 
 In addition to this, if D-Bus support is enabled, the PulseAudio client library
 (libpulse) MAY need to be licensed under the GPL, depending on the license
index 17ebc1d231f8c387e30e0aa25f04194f5b534b2d..0b81018665e3d2afe1c2bf3138d26b15a80b088b 100644 (file)
@@ -15,7 +15,6 @@ src/modules/alsa/module-alsa-card.c
 src/modules/alsa/module-alsa-sink.c
 src/modules/alsa/module-alsa-source.c
 src/modules/bluetooth/module-bluetooth-device.c
-src/modules/bluetooth/module-bluetooth-proximity.c
 src/modules/bluetooth/proximity-helper.c
 src/modules/echo-cancel/module-echo-cancel.c
 src/modules/gconf/gconf-helper.c
index 7fe84941dbda8b5b66b319cb779af16049cadb8a..80f6f2ce5ed03d3fbecb5556487e7008eb113cda 100644 (file)
@@ -7,7 +7,6 @@ TAGS
 .libs
 /Makefile
 /Makefile.in
-proximity-helper
 client.conf
 daemon.conf
 default.pa
index 9113d837e920bc3af27a40bd70031047f8e959f9..a294b65b8e265a4bb914e7068df1bdf69311d022 100644 (file)
@@ -1322,14 +1322,10 @@ endif
 
 if HAVE_BLUEZ
 modlibexec_LTLIBRARIES += \
-               module-bluetooth-proximity.la \
                module-bluetooth-policy.la \
                libbluez4-util.la \
                module-bluez4-discover.la \
                module-bluez4-device.la
-
-pulselibexec_PROGRAMS += \
-               proximity-helper
 endif
 
 if HAVE_OPENSSL
@@ -1418,7 +1414,6 @@ SYMDEF_FILES = \
                module-hal-detect-symdef.h \
                module-udev-detect-symdef.h \
                module-systemd-login-symdef.h \
-               module-bluetooth-proximity-symdef.h \
                module-bluetooth-policy-symdef.h \
                module-bluez4-discover-symdef.h \
                module-bluez4-device-symdef.h \
@@ -2017,17 +2012,6 @@ gconf_helper_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-
 gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
 gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
-# Bluetooth proximity
-module_bluetooth_proximity_la_SOURCES = modules/bluetooth/module-bluetooth-proximity.c
-module_bluetooth_proximity_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_bluetooth_proximity_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
-module_bluetooth_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/proximity-helper\"
-
-proximity_helper_SOURCES = modules/bluetooth/proximity-helper.c
-proximity_helper_LDADD = $(AM_LDADD) $(BLUEZ_LIBS)
-proximity_helper_CFLAGS = $(AM_CFLAGS) $(BLUEZ_CFLAGS)
-proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-
 # Bluetooth BlueZ 4 sink / source
 module_bluez4_discover_la_SOURCES = modules/bluetooth/module-bluez4-discover.c
 module_bluez4_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
@@ -2082,10 +2066,6 @@ else
 SYMLINK_PROGRAM=ln -sf
 endif
 install-exec-hook:
-if HAVE_BLUEZ
-       -chown root $(DESTDIR)$(pulselibexecdir)/proximity-helper
-       -chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper
-endif
        $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/parec$(EXEEXT)
        $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/pamon$(EXEEXT)
        $(SYMLINK_PROGRAM) pacat$(EXEEXT) $(DESTDIR)$(bindir)/paplay$(EXEEXT)
diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c
deleted file mode 100644 (file)
index 1110ceb..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2005-2006 Lennart Poettering
-
-  PulseAudio 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.
-
-  PulseAudio 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 Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <pulse/xmalloc.h>
-#include <pulsecore/module.h>
-#include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/start-child.h>
-#include <pulsecore/dbus-shared.h>
-
-#include "module-bluetooth-proximity-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(true);
-PA_MODULE_USAGE(
-        "sink=<sink name> "
-        "hci=<hci device> "
-);
-
-#define DEFAULT_HCI "hci0"
-
-static const char* const valid_modargs[] = {
-    "sink",
-    "hci",
-    NULL,
-};
-
-struct bonding {
-    struct userdata *userdata;
-    char address[18];
-
-    pid_t pid;
-    int fd;
-
-    pa_io_event *io_event;
-
-    enum {
-        UNKNOWN,
-        FOUND,
-        NOT_FOUND
-    } state;
-};
-
-struct userdata {
-    pa_module *module;
-    pa_dbus_connection *dbus_connection;
-
-    char *sink_name;
-    char *hci, *hci_path;
-
-    pa_hashmap *bondings;
-
-    unsigned n_found;
-    unsigned n_unknown;
-
-    bool muted:1;
-    bool filter_added:1;
-};
-
-static void update_volume(struct userdata *u) {
-    pa_assert(u);
-
-    if (u->muted && u->n_found > 0) {
-        pa_sink *s;
-
-        u->muted = false;
-
-        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
-            pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
-            return;
-        }
-
-        pa_log_info("Found %u BT devices, unmuting.", u->n_found);
-        pa_sink_set_mute(s, false, false);
-
-    } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
-        pa_sink *s;
-
-        u->muted = true;
-
-        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
-            pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
-            return;
-        }
-
-        pa_log_info("No BT devices found, muting.");
-        pa_sink_set_mute(s, true, false);
-
-    } else
-        pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
-}
-
-static void bonding_free(struct bonding *b) {
-    pa_assert(b);
-
-    if (b->state == FOUND)
-        pa_assert_se(b->userdata->n_found-- >= 1);
-
-    if (b->state == UNKNOWN)
-        pa_assert_se(b->userdata->n_unknown-- >= 1);
-
-    if (b->pid != (pid_t) -1) {
-        kill(b->pid, SIGTERM);
-        waitpid(b->pid, NULL, 0);
-    }
-
-    if (b->fd >= 0)
-        pa_close(b->fd);
-
-    if (b->io_event)
-        b->userdata->module->core->mainloop->io_free(b->io_event);
-
-    pa_xfree(b);
-}
-
-static void io_event_cb(
-        pa_mainloop_api*a,
-        pa_io_event* e,
-        int fd,
-        pa_io_event_flags_t events,
-        void *userdata) {
-
-    struct bonding *b = userdata;
-    char x;
-    ssize_t r;
-
-    pa_assert(b);
-
-    if ((r = read(fd, &x, 1)) <= 0) {
-        pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
-
-        pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
-        bonding_free(b);
-        return;
-    }
-
-    pa_assert_se(r == 1);
-
-    if (b->state == UNKNOWN)
-        pa_assert_se(b->userdata->n_unknown-- >= 1);
-
-    if (x == '+') {
-        pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
-
-        b->state = FOUND;
-        b->userdata->n_found++;
-
-        pa_log_info("Device '%s' is alive.", b->address);
-
-    } else {
-        pa_assert(x == '-');
-        pa_assert(b->state == UNKNOWN || b->state == FOUND);
-
-        if (b->state == FOUND)
-            b->userdata->n_found--;
-
-        b->state = NOT_FOUND;
-
-        pa_log_info("Device '%s' is dead.", b->address);
-    }
-
-    update_volume(b->userdata);
-}
-
-static struct bonding* bonding_new(struct userdata *u, const char *a) {
-    struct bonding *b = NULL;
-    DBusMessage *m = NULL, *r = NULL;
-    DBusError e;
-    const char *class;
-
-    pa_assert(u);
-    pa_assert(a);
-
-    pa_return_val_if_fail(strlen(a) == 17, NULL);
-    pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
-
-    dbus_error_init(&e);
-
-    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
-    pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
-    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
-
-    if (!r) {
-        pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
-        goto fail;
-    }
-
-    if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
-        pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
-        goto fail;
-    }
-
-    if (!pa_streq(class, "phone")) {
-        pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
-        goto fail;
-    }
-
-    b = pa_xnew(struct bonding, 1);
-    b->userdata = u;
-    pa_strlcpy(b->address, a, sizeof(b->address));
-    b->pid = (pid_t) -1;
-    b->fd = -1;
-    b->io_event = NULL;
-    b->state = UNKNOWN;
-    u->n_unknown ++;
-
-    pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
-
-    if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
-        pa_log("Failed to start helper tool.");
-        goto fail;
-    }
-
-    b->io_event = u->module->core->mainloop->io_new(
-            u->module->core->mainloop,
-            b->fd,
-            PA_IO_EVENT_INPUT,
-            io_event_cb,
-            b);
-
-    dbus_message_unref(m);
-    dbus_message_unref(r);
-
-    pa_hashmap_put(u->bondings, b->address, b);
-
-    return b;
-
-fail:
-    if (m)
-        dbus_message_unref(m);
-    if (r)
-        dbus_message_unref(r);
-
-    if (b)
-        bonding_free(b);
-
-    dbus_error_free(&e);
-    return NULL;
-}
-
-static void bonding_remove(struct userdata *u, const char *a) {
-    struct bonding *b;
-    pa_assert(u);
-
-    pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
-
-    pa_log_info("No longer watching device '%s'", b->address);
-    bonding_free(b);
-}
-
-static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
-    struct userdata *u = userdata;
-    DBusError e;
-
-    dbus_error_init(&e);
-
-    if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
-        const char *a;
-
-        if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
-            pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
-            goto finish;
-        }
-
-        bonding_new(u, a);
-
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-    } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
-
-        const char *a;
-
-        if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
-            pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
-            goto finish;
-        }
-
-        bonding_remove(u, a);
-
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-    }
-
-finish:
-
-    dbus_error_free(&e);
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int update_matches(struct userdata *u, bool add) {
-    char *filter1, *filter2;
-    DBusError e;
-    int r = -1;
-
-    pa_assert(u);
-    dbus_error_init(&e);
-
-    filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
-    filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
-
-    if (add) {
-        dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
-
-        if (dbus_error_is_set(&e)) {
-            pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
-            goto finish;
-        }
-    } else
-        dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, NULL);
-
-    if (add) {
-        dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
-
-        if (dbus_error_is_set(&e)) {
-            pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
-            dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, NULL);
-            goto finish;
-        }
-    } else
-        dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, NULL);
-
-    if (add) {
-        pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
-        u->filter_added = true;
-    } else if (u->filter_added)
-        dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
-
-    r = 0;
-
-finish:
-    pa_xfree(filter1);
-    pa_xfree(filter2);
-    dbus_error_free(&e);
-
-    return r;
-}
-
-int pa__init(pa_module*m) {
-    pa_modargs *ma = NULL;
-    struct userdata *u;
-    DBusError e;
-    DBusMessage *msg = NULL, *r = NULL;
-    DBusMessageIter iter, sub;
-
-    pa_assert(m);
-    dbus_error_init(&e);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->module = m;
-    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
-    u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
-    u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
-    u->bondings = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) bonding_free);
-
-    if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
-        pa_log("Failed to get D-Bus connection: %s", e.message);
-        goto fail;
-    }
-
-    if (update_matches(u, true) < 0)
-        goto fail;
-
-    pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
-
-    if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
-        pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
-        goto fail;
-    }
-
-    dbus_message_iter_init(r, &iter);
-
-    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
-        pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
-        goto fail;
-    }
-
-    dbus_message_iter_recurse(&iter, &sub);
-
-    while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
-        const char *a = NULL;
-
-        dbus_message_iter_get_basic(&sub, &a);
-        bonding_new(u, a);
-
-        dbus_message_iter_next(&sub);
-    }
-
-    dbus_message_unref(r);
-    dbus_message_unref(msg);
-
-    pa_modargs_free(ma);
-
-    if (pa_hashmap_size(u->bondings) == 0)
-        pa_log_warn("Warning: no phone device bonded.");
-
-    update_volume(u);
-
-    return 0;
-
-fail:
-
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(m);
-
-    dbus_error_free(&e);
-
-    if (msg)
-        dbus_message_unref(msg);
-
-    if (r)
-        dbus_message_unref(r);
-
-    return -1;
-}
-
-void pa__done(pa_module*m) {
-    struct userdata *u;
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->bondings)
-        pa_hashmap_free(u->bondings);
-
-    if (u->dbus_connection) {
-        update_matches(u, false);
-        pa_dbus_connection_unref(u->dbus_connection);
-    }
-
-    pa_xfree(u->sink_name);
-    pa_xfree(u->hci_path);
-    pa_xfree(u->hci);
-    pa_xfree(u);
-}
diff --git a/src/modules/bluetooth/proximity-helper.c b/src/modules/bluetooth/proximity-helper.c
deleted file mode 100644 (file)
index 3767f01..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Small SUID helper that allows us to ping a BT device. Borrows
- * heavily from bluez-utils' l2ping, which is licensed as GPL2+
- * and comes with a copyright like this:
- *
- *  Copyright (C) 2000-2001  Qualcomm Incorporated
- *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
- *  Copyright (C) 2002-2007  Marcel Holtmann <marcel@holtmann.org>
- *
- *
- *  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 <config.h>
-#endif
-
-#undef NDEBUG
-
-#include <assert.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/select.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
-
-#define PING_STRING "PulseAudio"
-#define IDENT 200
-#define TIMEOUT 4
-#define INTERVAL 2
-
-static void update_status(int found) {
-    static int status = -1;
-
-    if (!found && status != 0)
-        printf("-");
-    if (found && status <= 0)
-        printf("+");
-
-    fflush(stdout);
-    status = !!found;
-}
-
-int main(int argc, char *argv[]) {
-    struct sockaddr_l2 addr;
-    union {
-        l2cap_cmd_hdr hdr;
-        uint8_t buf[L2CAP_CMD_HDR_SIZE + sizeof(PING_STRING)];
-    }  packet;
-    int fd = -1;
-    uint8_t id = IDENT;
-    int connected = 0;
-
-    assert(argc == 2);
-
-    for (;;) {
-        fd_set fds;
-        struct timeval end;
-        ssize_t r;
-
-        if (!connected) {
-
-            if (fd >= 0)
-                close(fd);
-
-            if ((fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
-                fprintf(stderr, "socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP) failed: %s", strerror(errno));
-                goto finish;
-            }
-
-            memset(&addr, 0, sizeof(addr));
-            addr.l2_family = AF_BLUETOOTH;
-            bacpy(&addr.l2_bdaddr, BDADDR_ANY);
-
-            if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-                fprintf(stderr, "bind() failed: %s", strerror(errno));
-                goto finish;
-            }
-
-            memset(&addr, 0, sizeof(addr));
-            addr.l2_family = AF_BLUETOOTH;
-            str2ba(argv[1], &addr.l2_bdaddr);
-
-            if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-
-                if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
-                    update_status(0);
-                    sleep(INTERVAL);
-                    continue;
-                }
-
-                fprintf(stderr, "connect() failed: %s", strerror(errno));
-                goto finish;
-            }
-
-            connected = 1;
-        }
-
-        assert(connected);
-
-        memset(&packet, 0, sizeof(packet));
-        strcpy((char*) packet.buf + L2CAP_CMD_HDR_SIZE, PING_STRING);
-        packet.hdr.ident = id;
-        packet.hdr.len = htobs(sizeof(PING_STRING));
-        packet.hdr.code = L2CAP_ECHO_REQ;
-
-        if ((r = send(fd, &packet, sizeof(packet), 0)) < 0) {
-
-            if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
-                update_status(0);
-                connected = 0;
-                sleep(INTERVAL);
-                continue;
-            }
-
-            fprintf(stderr, "send() failed: %s", strerror(errno));
-            goto finish;
-        }
-
-        assert(r == sizeof(packet));
-
-        gettimeofday(&end, NULL);
-        end.tv_sec += TIMEOUT;
-
-        for (;;) {
-            struct timeval now, delta;
-
-            gettimeofday(&now, NULL);
-
-            if (timercmp(&end, &now, <=)) {
-                update_status(0);
-                connected = 0;
-                sleep(INTERVAL);
-                break;
-            }
-
-            timersub(&end, &now, &delta);
-
-            FD_ZERO(&fds);
-            FD_SET(fd, &fds);
-
-            if (select(fd+1, &fds, NULL, NULL, &delta) < 0) {
-                fprintf(stderr, "select() failed: %s", strerror(errno));
-                goto finish;
-            }
-
-            if ((r = recv(fd, &packet, sizeof(packet), 0)) <= 0) {
-
-                if (errno == EHOSTDOWN || errno == ECONNRESET || errno == ETIMEDOUT) {
-                    update_status(0);
-                    connected = 0;
-                    sleep(INTERVAL);
-                    break;
-                }
-
-                fprintf(stderr, "send() failed: %s", r == 0 ? "EOF" : strerror(errno));
-                goto finish;
-            }
-
-            assert(r >= L2CAP_CMD_HDR_SIZE);
-
-            if (packet.hdr.ident != id)
-                continue;
-
-            if (packet.hdr.code == L2CAP_ECHO_RSP || packet.hdr.code == L2CAP_COMMAND_REJ) {
-
-                if (++id >= 0xFF)
-                    id = IDENT;
-
-                update_status(1);
-                sleep(INTERVAL);
-                break;
-            }
-        }
-    }
-
-finish:
-
-    if (fd >= 0)
-        close(fd);
-
-    return 1;
-}