summaryrefslogtreecommitdiff
path: root/profiles/proximity/immalert.c
diff options
context:
space:
mode:
Diffstat (limited to 'profiles/proximity/immalert.c')
-rwxr-xr-xprofiles/proximity/immalert.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/profiles/proximity/immalert.c b/profiles/proximity/immalert.c
new file mode 100755
index 00000000..adf91404
--- /dev/null
+++ b/profiles/proximity/immalert.c
@@ -0,0 +1,458 @@
+/*
+ *
+ * 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 <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+ #include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+ struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db_attribute *imservice;
+#endif
+ 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;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_DEST_TYPE, dst_type,
+ BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+#endif
+
+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)
+{
+ const char *path, *alert_level_str;
+
+ if (!condev)
+ return;
+
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel");
+}
+
+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);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void imm_alert_alert_lvl_write(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct imm_alert_adapter *ia = user_data;
+ struct connected_device *condev = NULL;
+ uint8_t ecode = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct btd_device *device = NULL;
+
+ if (!value || len == 0) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset != 0) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(ia->adapter, &bdaddr, bdaddr_type);
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ /* Write value should be anyone of 0x00, 0x01, 0x02 */
+ if (value[0] > 0x02) {
+ ecode = 0x80;
+ goto done;
+ }
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(ia, device);
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value[0] != 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 (condev) {
+ if (value[0] != NO_ALERT) {
+ condev->alert_level = value[0];
+ imm_alert_emit_alert_signal(condev, value[0]);
+ } else {
+ imm_alert_emit_alert_signal(condev, value[0]);
+ imm_alert_disc_cb(condev);
+ }
+ }
+
+ DBG("alert level set to %d by device %p", value[0], device);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+ return;
+
+done:
+ error("Set immediate alert level for dev %p", device);
+ /* remove alerts by erroneous devices */
+ imm_alert_disc_cb(condev);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+ struct imm_alert_adapter *imadapter;
+ struct gatt_db_attribute *service, *charc;
+ struct gatt_db *db;
+
+ imadapter = g_new0(struct imm_alert_adapter, 1);
+ imadapter->adapter = adapter;
+
+ imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+
+ /* Immediate Alert Service */
+ bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+ service = gatt_db_add_service(db, &uuid, true, 3);
+ if (!service)
+ goto err;
+
+ imadapter->imservice = service;
+
+ /*
+ * Alert Level characteristic.
+ */
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+ charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ NULL,
+ imm_alert_alert_lvl_write, imadapter);
+ if (!charc)
+ goto err;
+
+ gatt_db_service_set_active(service, true);
+
+ DBG("Immediate Alert service added");
+ return;
+err:
+ DBG("Error adding Immediate Alert service");
+ imm_alert_unregister(adapter);
+}
+#else
+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)
+{
+ 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;
+
+ 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_UUID16, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ GATT_CHR_PROP_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");
+}
+#endif
+
+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;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db *db;
+#endif
+
+ imadapter = find_imm_alert_adapter(adapter);
+ if (!imadapter)
+ return;
+
+ g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+ NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Remove registered service */
+ if (imadapter->imservice) {
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+ gatt_db_remove_service(db, imadapter->imservice);
+ }
+#endif
+
+ imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+ g_free(imadapter);
+}