289 lines
6.8 KiB
C
289 lines
6.8 KiB
C
|
/*
|
||
|
*
|
||
|
* 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 <gdbus/gdbus.h>
|
||
|
|
||
|
#include "lib/uuid.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"
|
||
|
|
||
|
#include "reporter.h"
|
||
|
#include "immalert.h"
|
||
|
|
||
|
struct imm_alert_adapter {
|
||
|
struct btd_adapter *adapter;
|
||
|
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)
|
||
|
{
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
|
||
|
g_free(imadapter);
|
||
|
}
|