summaryrefslogtreecommitdiff
path: root/proximity
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-10-30 12:51:59 -0700
committerAnas Nashif <anas.nashif@intel.com>2012-10-30 12:51:59 -0700
commit71235d13fb107f879369e64385d8c97de0bb840e (patch)
tree6610983e8decd3f2543f4d05c8e0794b260769ee /proximity
downloadbluez-71235d13fb107f879369e64385d8c97de0bb840e.tar.gz
bluez-71235d13fb107f879369e64385d8c97de0bb840e.tar.bz2
bluez-71235d13fb107f879369e64385d8c97de0bb840e.zip
Imported Upstream version 4.101upstream/4.101
Diffstat (limited to 'proximity')
-rw-r--r--proximity/immalert.c290
-rw-r--r--proximity/immalert.h26
-rw-r--r--proximity/linkloss.c338
-rw-r--r--proximity/linkloss.h26
-rw-r--r--proximity/main.c96
-rw-r--r--proximity/manager.c150
-rw-r--r--proximity/manager.h26
-rw-r--r--proximity/monitor.c667
-rw-r--r--proximity/monitor.h34
-rw-r--r--proximity/proximity.conf9
-rw-r--r--proximity/reporter.c306
-rw-r--r--proximity/reporter.h42
12 files changed, 2010 insertions, 0 deletions
diff --git a/proximity/immalert.c b/proximity/immalert.c
new file mode 100644
index 00000000..1540b613
--- /dev/null
+++ b/proximity/immalert.c
@@ -0,0 +1,290 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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 <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+ struct btd_adapter *adapter;
+ DBusConnection *conn;
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct imm_alert_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *condev = a;
+ const struct btd_device *device = b;
+
+ if (condev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(ia->connected_devices, device,
+ imdevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct imm_alert_adapter *imadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (imadapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+ imadapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+ struct imm_alert_adapter *imadapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ imadapter = find_imm_alert_adapter(device_get_adapter(device));
+ if (!imadapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(imadapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+ uint8_t alert_level)
+{
+ struct imm_alert_adapter *adapter;
+ const char *path, *alert_level_str;
+
+ if (!condev)
+ return;
+
+ adapter = condev->adapter;
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ emit_property_changed(adapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+ struct imm_alert_adapter *ia;
+
+ if (!condev)
+ return;
+
+ ia = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+ g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ if (!condev)
+ return;
+
+ DBG("immediate alert remove device %p", condev->device);
+
+ imm_alert_emit_alert_signal(condev, NO_ALERT);
+ imm_alert_remove_condev(condev);
+}
+
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct imm_alert_adapter *ia = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ condev = find_connected_device(ia, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = ia;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, imm_alert_disc_cb, condev);
+ ia->connected_devices = g_slist_append(ia->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+ }
+
+ if (value != NO_ALERT) {
+ condev->alert_level = value;
+ imm_alert_emit_alert_signal(condev, value);
+ }
+
+ /*
+ * Emit NO_ALERT if the alert level was non-zero before. This is
+ * guaranteed when there's a condev.
+ */
+ if (value == NO_ALERT && condev)
+ imm_alert_disc_cb(condev);
+
+ DBG("alert level set to %d by device %p", value, device);
+ return 0;
+
+set_error:
+ error("Set immediate alert level for dev %p", device);
+ /* remove alerts by erroneous devices */
+ imm_alert_disc_cb(condev);
+ return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct imm_alert_adapter *imadapter;
+
+ bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+ imadapter = g_new0(struct imm_alert_adapter, 1);
+ imadapter->adapter = adapter;
+ imadapter->conn = dbus_connection_ref(conn);
+
+ imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+ /* Immediate Alert Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ imm_alert_alert_lvl_write, imadapter,
+ GATT_OPT_INVALID);
+
+ if (!svc_added) {
+ imm_alert_unregister(adapter);
+ return;
+ }
+
+ DBG("Immediate Alert service added");
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+ struct imm_alert_adapter *imadapter;
+
+ imadapter = find_imm_alert_adapter(adapter);
+ if (!imadapter)
+ return;
+
+ g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+ NULL);
+ dbus_connection_unref(imadapter->conn);
+
+ imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+ g_free(imadapter);
+}
diff --git a/proximity/immalert.h b/proximity/immalert.h
new file mode 100644
index 00000000..dd28eaab
--- /dev/null
+++ b/proximity/immalert.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
diff --git a/proximity/linkloss.c b/proximity/linkloss.c
new file mode 100644
index 00000000..14403cbc
--- /dev/null
+++ b/proximity/linkloss.c
@@ -0,0 +1,338 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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 <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "att-database.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "linkloss.h"
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct link_loss_adapter {
+ struct btd_adapter *adapter;
+ uint16_t alert_lvl_value_handle;
+ DBusConnection *conn;
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct link_loss_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+ guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *llcondev = a;
+ const struct btd_device *device = b;
+
+ if (llcondev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(la->connected_devices, device,
+ lldevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct link_loss_adapter *lladapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (lladapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+ lladapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+ struct link_loss_adapter *lladapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ lladapter = find_link_loss_adapter(device_get_adapter(device));
+ if (!lladapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(lladapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+ struct link_loss_adapter *adapter = condev->adapter;
+ const char *alert_level_str, *path;
+
+ if (!condev->device)
+ return;
+
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(condev->alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ emit_property_changed(adapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev;
+ uint8_t alert_level = NO_ALERT;
+
+ if (!device)
+ goto out;
+
+ condev = find_connected_device(la, device);
+ if (!condev)
+ goto out;
+
+ alert_level = condev->alert_level;
+
+out:
+ DBG("return alert level %d for dev %p", alert_level, device);
+
+ /* update the alert level according to the requesting device */
+ attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+ sizeof(alert_level), NULL);
+
+ return 0;
+}
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+ struct link_loss_adapter *la;
+
+ if (!condev)
+ return;
+
+ la = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->local_disc_id && condev->device)
+ device_remove_disconnect_watch(condev->device,
+ condev->local_disc_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ la->connected_devices = g_slist_remove(la->connected_devices, condev);
+ g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ DBG("alert loss disconnect device %p", condev->device);
+
+ /* if an alert-level is set, emit a signal */
+ if (condev->alert_level != NO_ALERT)
+ link_loss_emit_alert_signal(condev);
+
+ /* we are open for more changes now */
+ link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+ gboolean removal, void *user_data)
+{
+ struct connected_device *condev = user_data;
+
+ /* no need to alert on this device - we requested disconnection */
+ link_loss_remove_condev(condev);
+
+ DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(la, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = la;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, link_loss_disc_cb, condev);
+ condev->local_disc_id = device_add_disconnect_watch(device,
+ link_loss_local_disc, condev, NULL);
+
+ la->connected_devices = g_slist_append(la->connected_devices,
+ condev);
+ } else if (value == NO_ALERT && condev) {
+ link_loss_remove_condev(condev);
+ condev = NULL;
+ }
+
+ DBG("alert level set to %d by device %p", value, device);
+
+ if (condev)
+ condev->alert_level = value;
+
+ return 0;
+
+set_error:
+ error("Set link loss alert level for dev %p", device);
+ /* reset alert level on erroneous devices */
+ link_loss_remove_condev(condev);
+ return ATT_ECODE_IO;
+}
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct link_loss_adapter *lladapter;
+
+ bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+ lladapter = g_new0(struct link_loss_adapter, 1);
+ lladapter->adapter = adapter;
+ lladapter->conn = dbus_connection_ref(conn);
+
+ link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+
+ /* Link Loss Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ link_loss_alert_lvl_read, lladapter,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ link_loss_alert_lvl_write, lladapter,
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &lladapter->alert_lvl_value_handle,
+ GATT_OPT_INVALID);
+
+ if (!svc_added)
+ goto err;
+
+ DBG("Link Loss service added");
+ return;
+
+err:
+ error("Error adding Link Loss service");
+ link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+ struct link_loss_adapter *lladapter;
+ lladapter = find_link_loss_adapter(adapter);
+ if (!lladapter)
+ return;
+
+ g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+ NULL);
+ dbus_connection_unref(lladapter->conn);
+
+ link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+ g_free(lladapter);
+}
diff --git a/proximity/linkloss.h b/proximity/linkloss.h
new file mode 100644
index 00000000..a7d83d02
--- /dev/null
+++ b/proximity/linkloss.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
diff --git a/proximity/main.c b/proximity/main.c
new file mode 100644
index 00000000..3d5d9b2d
--- /dev/null
+++ b/proximity/main.c
@@ -0,0 +1,96 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+
+#include <errno.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+
+static DBusConnection *connection = NULL;
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+ GError *gerr = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+ error("Parsing %s failed: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static int proximity_init(void)
+{
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ config = open_config_file(CONFIGDIR "/proximity.conf");
+
+ if (proximity_manager_init(connection, config) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void proximity_exit(void)
+{
+ if (!main_opts.gatt_enabled)
+ return;
+
+ if (config)
+ g_key_file_free(config);
+
+ proximity_manager_exit();
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ proximity_init, proximity_exit)
diff --git a/proximity/manager.c b/proximity/manager.c
new file mode 100644
index 00000000..f2e49a6c
--- /dev/null
+++ b/proximity/manager.c
@@ -0,0 +1,150 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+
+#include <glib.h>
+#include <gdbus.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+static DBusConnection *connection = NULL;
+
+static struct enabled enabled = {
+ .linkloss = TRUE,
+ .pathloss = TRUE,
+ .findme = TRUE,
+};
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
+static int attio_device_probe(struct btd_device *device, GSList *uuids)
+{
+ struct gatt_primary *linkloss, *txpower, *immediate;
+ GSList *l, *primaries;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, IMMEDIATE_ALERT_UUID,
+ primary_uuid_cmp);
+ immediate = (l ? l->data : NULL);
+
+ l = g_slist_find_custom(primaries, TX_POWER_UUID, primary_uuid_cmp);
+ txpower = (l ? l->data : NULL);
+
+ l = g_slist_find_custom(primaries, LINK_LOSS_UUID, primary_uuid_cmp);
+ linkloss = (l ? l->data : NULL);
+
+ return monitor_register(connection, device, linkloss, txpower,
+ immediate, &enabled);
+}
+
+static void attio_device_remove(struct btd_device *device)
+{
+ monitor_unregister(connection, device);
+}
+
+static struct btd_device_driver monitor_driver = {
+ .name = "Proximity GATT Monitor Driver",
+ .uuids = BTD_UUIDS(IMMEDIATE_ALERT_UUID, LINK_LOSS_UUID, TX_POWER_UUID),
+ .probe = attio_device_probe,
+ .remove = attio_device_remove,
+};
+
+static struct btd_adapter_driver reporter_server_driver = {
+ .name = "Proximity GATT Reporter Driver",
+ .probe = reporter_init,
+ .remove = reporter_exit,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+ char **list;
+ int i;
+
+ if (config == NULL)
+ return;
+
+ 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], "FindMe"))
+ enabled.findme = FALSE;
+ else if (g_str_equal(list[i], "LinkLoss"))
+ enabled.linkloss = FALSE;
+ else if (g_str_equal(list[i], "PathLoss"))
+ enabled.pathloss = FALSE;
+ }
+
+ g_strfreev(list);
+}
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *config)
+{
+ int ret;
+
+ load_config_file(config);
+
+ connection = dbus_connection_ref(conn);
+
+ ret = btd_register_device_driver(&monitor_driver);
+ if (ret < 0)
+ goto fail_monitor;
+
+ ret = btd_register_adapter_driver(&reporter_server_driver);
+ if (ret < 0)
+ goto fail_reporter;
+
+ return 0;
+
+fail_reporter:
+ btd_unregister_device_driver(&monitor_driver);
+
+fail_monitor:
+ dbus_connection_unref(connection);
+ return ret;
+}
+
+void proximity_manager_exit(void)
+{
+ btd_unregister_device_driver(&monitor_driver);
+ btd_unregister_adapter_driver(&reporter_server_driver);
+ dbus_connection_unref(connection);
+}
diff --git a/proximity/manager.h b/proximity/manager.h
new file mode 100644
index 00000000..b0fe7c8c
--- /dev/null
+++ b/proximity/manager.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+ *
+ */
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *conf);
+void proximity_manager_exit(void);
diff --git a/proximity/monitor.c b/proximity/monitor.c
new file mode 100644
index 00000000..98dbcd10
--- /dev/null
+++ b/proximity/monitor.c
@@ -0,0 +1,667 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+
+#include <errno.h>
+#include <fcntl.h>
+#include <gdbus.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "error.h"
+#include "log.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "attio.h"
+#include "monitor.h"
+#include "textfile.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT 5
+
+enum {
+ ALERT_NONE = 0,
+ ALERT_MILD,
+ ALERT_HIGH,
+};
+
+struct monitor {
+ struct btd_device *device;
+ GAttrib *attrib;
+ DBusConnection *conn;
+ struct att_range *linkloss;
+ struct att_range *txpower;
+ struct att_range *immediate;
+ struct enabled enabled;
+ char *linklosslevel; /* Link Loss Alert Level */
+ char *fallbacklevel; /* Immediate fallback alert level */
+ char *immediatelevel; /* Immediate Alert Level */
+ char *signallevel; /* Path Loss RSSI level */
+ uint16_t linklosshandle; /* Link Loss Characteristic
+ * Value Handle */
+ uint16_t txpowerhandle; /* Tx Characteristic Value Handle */
+ uint16_t immediatehandle; /* Immediate Alert Value Handle */
+ guint immediateto; /* Reset Immediate Alert to "none" */
+ guint attioid;
+};
+
+static inline int create_filename(char *buf, size_t size,
+ const bdaddr_t *bdaddr, const char *name)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+
+ return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+ const char *alert, const char *level)
+{
+ char filename[PATH_MAX + 1], addr[18], key[38];
+
+ create_filename(filename, PATH_MAX, sba, "proximity");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+ return textfile_put(filename, key, level);
+}
+
+static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+ const char *alert)
+{
+ char filename[PATH_MAX + 1], addr[18], key[38];
+ char *str, *strnew;
+
+ create_filename(filename, PATH_MAX, sba, "proximity");
+
+ ba2str(dba, addr);
+ snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+ str = textfile_caseget(filename, key);
+ if (str == NULL)
+ return NULL;
+
+ strnew = g_strdup(str);
+ free(str);
+
+ return strnew;
+}
+
+static uint8_t str2level(const char *level)
+{
+ if (g_strcmp0("high", level) == 0)
+ return ALERT_HIGH;
+ else if (g_strcmp0("mild", level) == 0)
+ return ALERT_MILD;
+
+ return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct btd_device *device = monitor->device;
+ const char *path = device_get_path(device);
+
+ if (status != 0) {
+ error("Link Loss Write Request failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_write_resp(pdu, plen)) {
+ error("Link Loss Write Request: protocol error");
+ return;
+ }
+
+ DBG("Link Loss Alert Level written");
+
+ emit_property_changed(monitor->conn, path,
+ PROXIMITY_INTERFACE, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &monitor->linklosslevel);
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ if (status) {
+ error("Discover Link Loss handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+ /* Assume there is a single Alert Level characteristic */
+ chr = characteristics->data;
+ monitor->linklosshandle = chr->value_handle;
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+ linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+ struct att_range *linkloss = monitor->linkloss;
+ bt_uuid_t uuid;
+
+ if (monitor->linklosshandle) {
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle,
+ &value, 1, linkloss_written, monitor);
+ return 0;
+ }
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ /* FIXME: use cache (requires service changed support) ? */
+ gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+ &uuid, char_discovered_cb, monitor);
+
+ return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint8_t value[ATT_MAX_MTU];
+ int vlen;
+
+ if (status != 0) {
+ DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, plen, value, &vlen)) {
+ DBG("Protocol error");
+ return;
+ }
+
+ if (vlen != 1) {
+ DBG("Invalid length for TX Power value: %d", vlen);
+ return;
+ }
+
+ DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+
+ if (status) {
+ error("Discover Tx Power handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->txpowerhandle = chr->value_handle;
+
+ DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+ tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+ struct att_range *txpower = monitor->txpower;
+ bt_uuid_t uuid;
+
+ if (monitor->txpowerhandle != 0) {
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+ tx_power_read_cb, monitor);
+ return;
+ }
+
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+ &uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return FALSE;
+
+ if (monitor->attrib) {
+ uint8_t value = ALERT_NONE;
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+ &value, 1, NULL, NULL);
+ }
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &monitor->immediatelevel);
+
+ return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = NULL;
+
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+ monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+ immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+ uint8_t value = str2level(monitor->immediatelevel);
+
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+ immediate_written, monitor);
+}
+
+static void immediate_handle_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+
+ if (status) {
+ error("Discover Immediate Alert handle: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->immediatehandle = chr->value_handle;
+
+ DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+ if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+ struct att_range *immediate = monitor->immediate;
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+ &uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ monitor->attrib = g_attrib_ref(attrib);
+
+ if (monitor->enabled.linkloss)
+ write_alert_level(monitor);
+
+ if (monitor->enabled.pathloss)
+ read_tx_power(monitor);
+
+ if (monitor->immediatehandle == 0) {
+ if(monitor->enabled.pathloss || monitor->enabled.findme)
+ discover_immediate_handle(monitor);
+ } else if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_attrib_unref(monitor->attrib);
+ monitor->attrib = NULL;
+
+ if (monitor->immediateto == 0)
+ return;
+
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return;
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &monitor->immediatelevel);
+}
+
+static gboolean level_is_valid(const char *level)
+{
+ return (g_str_equal("none", level) ||
+ g_str_equal("mild", level) ||
+ g_str_equal("high", level));
+}
+
+static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
+ const char *level, void *data)
+{
+ struct monitor *monitor = data;
+ struct btd_device *device = monitor->device;
+ bdaddr_t sba, dba;
+
+ if (!level_is_valid(level))
+ return btd_error_invalid_args(msg);
+
+ if (g_strcmp0(monitor->linklosslevel, level) == 0)
+ return dbus_message_new_method_return(msg);
+
+ g_free(monitor->linklosslevel);
+ monitor->linklosslevel = g_strdup(level);
+
+ adapter_get_address(device_get_adapter(device), &sba);
+ device_get_address(device, &dba, NULL);
+
+ write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
+
+ if (monitor->attrib)
+ write_alert_level(monitor);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
+ const char *level, void *data)
+{
+ struct monitor *monitor = data;
+
+ if (!level_is_valid(level))
+ return btd_error_invalid_args(msg);
+
+ if (g_strcmp0(monitor->immediatelevel, level) == 0)
+ return dbus_message_new_method_return(msg);
+
+ if (monitor->immediateto) {
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+ }
+
+ /* Previous Immediate Alert level if connection/write fails */
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = monitor->immediatelevel;
+
+ monitor->immediatelevel = g_strdup(level);
+
+ /*
+ * Means that Link/Path Loss are disabled or there is a pending
+ * writting for Find Me(Immediate Alert characteristic value).
+ * If enabled, Path Loss always registers a connection callback
+ * when the Proximity Monitor starts.
+ */
+ if (monitor->attioid == 0)
+ monitor->attioid = btd_device_add_attio_callback(monitor->device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+ else if (monitor->attrib)
+ write_immediate_alert(monitor);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct monitor *monitor = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (monitor->enabled.linkloss)
+ dict_append_entry(&dict, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &monitor->linklosslevel);
+
+ if (monitor->enabled.findme || monitor->enabled.pathloss)
+ dict_append_entry(&dict, "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+ if (monitor->enabled.pathloss)
+ dict_append_entry(&dict, "SignalLevel",
+ DBUS_TYPE_STRING, &monitor->signallevel);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct monitor *monitor = data;
+ const char *property;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *level;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &level);
+
+ if (g_str_equal("ImmediateAlertLevel", property)) {
+ if (monitor->enabled.findme == FALSE &&
+ monitor->enabled.pathloss == FALSE)
+ return btd_error_not_available(msg);
+
+ return set_immediate_alert(conn, msg, level, data);
+ } else if (g_str_equal("LinkLossAlertLevel", property)) {
+ if (monitor->enabled.linkloss == FALSE)
+ return btd_error_not_available(msg);
+
+ return set_link_loss_alert(conn, msg, level, data);
+ }
+
+ return btd_error_invalid_args(msg);
+}
+
+static const GDBusMethodTable monitor_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_ASYNC_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
+ { }
+};
+
+static const GDBusSignalTable monitor_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ if (monitor->immediateto)
+ g_source_remove(monitor->immediateto);
+
+ if (monitor->attioid)
+ btd_device_remove_attio_callback(monitor->device,
+ monitor->attioid);
+ if (monitor->attrib)
+ g_attrib_unref(monitor->attrib);
+
+ dbus_connection_unref(monitor->conn);
+ btd_device_unref(monitor->device);
+ g_free(monitor->linkloss);
+ g_free(monitor->immediate);
+ g_free(monitor->txpower);
+ g_free(monitor->linklosslevel);
+ g_free(monitor->immediatelevel);
+ g_free(monitor->signallevel);
+ g_free(monitor);
+}
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+ struct gatt_primary *linkloss, struct gatt_primary *txpower,
+ struct gatt_primary *immediate, struct enabled *enabled)
+{
+ const char *path = device_get_path(device);
+ struct monitor *monitor;
+ bdaddr_t sba, dba;
+ char *level;
+
+ adapter_get_address(device_get_adapter(device), &sba);
+ device_get_address(device, &dba, NULL);
+
+ level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
+
+ monitor = g_new0(struct monitor, 1);
+ monitor->device = btd_device_ref(device);
+ monitor->conn = dbus_connection_ref(conn);
+ monitor->linklosslevel = (level ? : g_strdup("high"));
+ monitor->signallevel = g_strdup("unknown");
+ monitor->immediatelevel = g_strdup("none");
+
+ if (g_dbus_register_interface(conn, path,
+ PROXIMITY_INTERFACE,
+ monitor_methods, monitor_signals,
+ NULL, monitor, monitor_destroy) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ PROXIMITY_INTERFACE);
+ monitor_destroy(monitor);
+ return -1;
+ }
+
+ DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+ if (linkloss && enabled->linkloss) {
+ monitor->linkloss = g_new0(struct att_range, 1);
+ monitor->linkloss->start = linkloss->range.start;
+ monitor->linkloss->end = linkloss->range.end;
+
+ monitor->enabled.linkloss = TRUE;
+ }
+
+ if (immediate) {
+ if (txpower && enabled->pathloss) {
+ monitor->txpower = g_new0(struct att_range, 1);
+ monitor->txpower->start = txpower->range.start;
+ monitor->txpower->end = txpower->range.end;
+
+ monitor->enabled.pathloss = TRUE;
+ }
+
+ if (enabled->pathloss || enabled->findme) {
+ monitor->immediate = g_new0(struct att_range, 1);
+ monitor->immediate->start = immediate->range.start;
+ monitor->immediate->end = immediate->range.end;
+ }
+
+ monitor->enabled.findme = enabled->findme;
+ }
+
+ DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+ monitor->enabled.linkloss ? "TRUE" : "FALSE",
+ monitor->enabled.pathloss ? "TRUE" : "FALSE",
+ monitor->enabled.findme ? "TRUE" : "FALSE");
+
+ if (monitor->enabled.linkloss || monitor->enabled.pathloss)
+ monitor->attioid = btd_device_add_attio_callback(device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+
+ return 0;
+}
+
+void monitor_unregister(DBusConnection *conn, struct btd_device *device)
+{
+ const char *path = device_get_path(device);
+
+ g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);
+}
diff --git a/proximity/monitor.h b/proximity/monitor.h
new file mode 100644
index 00000000..b71363d6
--- /dev/null
+++ b/proximity/monitor.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+ *
+ */
+
+struct enabled {
+ gboolean linkloss;
+ gboolean pathloss;
+ gboolean findme;
+};
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+ struct gatt_primary *linkloss, struct gatt_primary *txpower,
+ struct gatt_primary *immediate, struct enabled *enabled);
+void monitor_unregister(DBusConnection *conn, struct btd_device *device);
diff --git a/proximity/proximity.conf b/proximity/proximity.conf
new file mode 100644
index 00000000..417610ff
--- /dev/null
+++ b/proximity/proximity.conf
@@ -0,0 +1,9 @@
+# Configuration file for the proximity service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Configuration to allow disabling Proximity services
+# Allowed values: LinkLoss,PathLoss,FindMe
+Disable=PathLoss
diff --git a/proximity/reporter.c b/proximity/reporter.c
new file mode 100644
index 00000000..607de2bd
--- /dev/null
+++ b/proximity/reporter.c
@@ -0,0 +1,306 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "dbus-common.h"
+#include "error.h"
+#include "device.h"
+#include "hcid.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "attrib-server.h"
+#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.h"
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct reporter_adapter {
+ DBusConnection *conn;
+ struct btd_adapter *adapter;
+ GSList *devices;
+};
+
+static GSList *reporter_adapters;
+
+static int radapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct reporter_adapter *radapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (radapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+ radapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *get_alert_level_string(uint8_t level)
+{
+ switch (level) {
+ case NO_ALERT:
+ return "none";
+ case MILD_ALERT:
+ return "mild";
+ case HIGH_ALERT:
+ return "high";
+ }
+
+ return "unknown";
+}
+
+static void register_tx_power(struct btd_adapter *adapter)
+{
+ uint16_t start_handle, h;
+ const int svc_size = 4;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Power level characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Power level value */
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+ att_put_u8(0x00, &atval[0]);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+ /* Client characteristic configuration */
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+ g_assert(h - start_handle == svc_size);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply = NULL;
+ const char *linkloss_level, *immalert_level;
+ struct btd_device *device = data;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ linkloss_level = link_loss_get_alert_level(device);
+ immalert_level = imm_alert_get_level(device);
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict))
+ goto err;
+
+ dict_append_entry(&dict, "LinkLossAlertLevel", DBUS_TYPE_STRING,
+ &linkloss_level);
+ dict_append_entry(&dict, "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &immalert_level);
+
+ if (!dbus_message_iter_close_container(&iter, &dict))
+ goto err;
+
+ return reply;
+
+err:
+ if (reply)
+ dbus_message_unref(reply);
+ return btd_error_failed(msg, "not enough memory");
+}
+
+static const GDBusMethodTable reporter_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { }
+};
+
+static const GDBusSignalTable reporter_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+ struct btd_device *device = data;
+ struct reporter_adapter *radapter = user_data;
+ const char *path = device_get_path(device);
+
+ DBG("unregister on device %s", path);
+
+ g_dbus_unregister_interface(radapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE);
+
+ radapter->devices = g_slist_remove(radapter->devices, device);
+ btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+ struct reporter_adapter *radapter)
+{
+ const char *path = device_get_path(device);
+
+ DBG("register on device %s", path);
+
+ g_dbus_register_interface(radapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE,
+ reporter_methods, reporter_signals,
+ NULL, device, NULL);
+
+ btd_device_ref(device);
+ radapter->devices = g_slist_prepend(radapter->devices, device);
+}
+
+static int reporter_device_probe(struct btd_device *device, GSList *uuids)
+{
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return -1;
+
+ register_reporter_device(device, radapter);
+ return 0;
+}
+
+static void reporter_device_remove(struct btd_device *device)
+{
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ unregister_reporter_device(device, radapter);
+}
+
+/* device driver for tracking remote GATT client devices */
+static struct btd_device_driver reporter_device_driver = {
+ .name = "Proximity GATT Reporter Device Tracker Driver",
+ .uuids = BTD_UUIDS(GATT_UUID),
+ .probe = reporter_device_probe,
+ .remove = reporter_device_remove,
+};
+
+int reporter_init(struct btd_adapter *adapter)
+{
+ struct reporter_adapter *radapter;
+ DBusConnection *conn;
+
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn)
+ return -1;
+
+ radapter = g_new0(struct reporter_adapter, 1);
+ radapter->adapter = adapter;
+ radapter->conn = conn;
+
+ link_loss_register(adapter, radapter->conn);
+ register_tx_power(adapter);
+ imm_alert_register(adapter, radapter->conn);
+
+ btd_register_device_driver(&reporter_device_driver);
+
+ reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+ DBG("Proximity Reporter for adapter %p", adapter);
+
+ return 0;
+}
+
+void reporter_exit(struct btd_adapter *adapter)
+{
+ struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ btd_unregister_device_driver(&reporter_device_driver);
+
+ g_slist_foreach(radapter->devices, unregister_reporter_device,
+ radapter);
+
+ link_loss_unregister(adapter);
+ imm_alert_unregister(adapter);
+ dbus_connection_unref(radapter->conn);
+
+ reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+ g_free(radapter);
+}
diff --git a/proximity/reporter.h b/proximity/reporter.h
new file mode 100644
index 00000000..5ae0eb2f
--- /dev/null
+++ b/proximity/reporter.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 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
+ *
+ */
+
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter"
+
+#define IMMEDIATE_ALERT_SVC_UUID 0x1802
+#define LINK_LOSS_SVC_UUID 0x1803
+#define TX_POWER_SVC_UUID 0x1804
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+enum {
+ NO_ALERT = 0x00,
+ MILD_ALERT = 0x01,
+ HIGH_ALERT = 0x02,
+};
+
+int reporter_init(struct btd_adapter *adapter);
+void reporter_exit(struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level);