820 lines
19 KiB
C
820 lines
19 KiB
C
/*
|
|
*
|
|
* 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/gdbus.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <glib.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
|
|
#include "lib/uuid.h"
|
|
#include "src/dbus-common.h"
|
|
#include "src/adapter.h"
|
|
#include "src/device.h"
|
|
#include "src/error.h"
|
|
#include "src/log.h"
|
|
#include "attrib/att.h"
|
|
#include "attrib/gattrib.h"
|
|
#include "attrib/gatt.h"
|
|
#include "src/attio.h"
|
|
#include "src/textfile.h"
|
|
|
|
#include "monitor.h"
|
|
|
|
#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1"
|
|
|
|
#define ALERT_LEVEL_CHR_UUID 0x2A06
|
|
#define POWER_LEVEL_CHR_UUID 0x2A07
|
|
|
|
#define IMMEDIATE_TIMEOUT 5
|
|
#define TX_POWER_SIZE 1
|
|
|
|
enum {
|
|
ALERT_NONE = 0,
|
|
ALERT_MILD,
|
|
ALERT_HIGH,
|
|
};
|
|
|
|
struct monitor {
|
|
struct btd_device *device;
|
|
GAttrib *attrib;
|
|
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 GSList *monitors = NULL;
|
|
|
|
static struct monitor *find_monitor(struct btd_device *device)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = monitors; l; l = l->next) {
|
|
struct monitor *monitor = l->data;
|
|
|
|
if (monitor->device == device)
|
|
return monitor;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void write_proximity_config(struct btd_device *device, const char *alert,
|
|
const char *level)
|
|
{
|
|
char *filename;
|
|
GKeyFile *key_file;
|
|
char *data;
|
|
gsize length = 0;
|
|
|
|
filename = btd_device_get_storage_path(device, "proximity");
|
|
if (!filename) {
|
|
warn("Unable to get proximity storage path for device");
|
|
return;
|
|
}
|
|
|
|
key_file = g_key_file_new();
|
|
g_key_file_load_from_file(key_file, filename, 0, NULL);
|
|
|
|
if (level)
|
|
g_key_file_set_string(key_file, alert, "Level", level);
|
|
else
|
|
g_key_file_remove_group(key_file, alert, NULL);
|
|
|
|
data = g_key_file_to_data(key_file, &length, NULL);
|
|
if (length > 0) {
|
|
create_file(filename, S_IRUSR | S_IWUSR);
|
|
g_file_set_contents(filename, data, length, NULL);
|
|
}
|
|
|
|
g_free(data);
|
|
g_free(filename);
|
|
g_key_file_free(key_file);
|
|
}
|
|
|
|
static char *read_proximity_config(struct btd_device *device, const char *alert)
|
|
{
|
|
char *filename;
|
|
GKeyFile *key_file;
|
|
char *str;
|
|
|
|
filename = btd_device_get_storage_path(device, "proximity");
|
|
if (!filename) {
|
|
warn("Unable to get proximity storage path for device");
|
|
return NULL;
|
|
}
|
|
|
|
key_file = g_key_file_new();
|
|
g_key_file_load_from_file(key_file, filename, 0, NULL);
|
|
|
|
str = g_key_file_get_string(key_file, alert, "Level", NULL);
|
|
|
|
g_free(filename);
|
|
g_key_file_free(key_file);
|
|
|
|
return str;
|
|
}
|
|
|
|
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");
|
|
|
|
g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE, "LinkLossAlertLevel");
|
|
}
|
|
|
|
static void char_discovered_cb(uint8_t status, GSList *characteristics,
|
|
void *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[TX_POWER_SIZE];
|
|
ssize_t vlen;
|
|
|
|
if (status != 0) {
|
|
DBG("Tx Power Level read failed: %s", att_ecode2str(status));
|
|
return;
|
|
}
|
|
|
|
vlen = dec_read_resp(pdu, plen, value, sizeof(value));
|
|
if (vlen < 0) {
|
|
DBG("Protocol error");
|
|
return;
|
|
}
|
|
|
|
if (vlen != 1) {
|
|
DBG("Invalid length for TX Power value: %zd", vlen);
|
|
return;
|
|
}
|
|
|
|
DBG("Tx Power Level: %02x", (int8_t) value[0]);
|
|
}
|
|
|
|
static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
|
|
void *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,
|
|
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,
|
|
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");
|
|
|
|
|
|
g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE, "ImmediateAlertLevel");
|
|
|
|
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;
|
|
|
|
|
|
g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE, "ImmediateAlertLevel");
|
|
|
|
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(uint8_t status, GSList *characteristics,
|
|
void *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");
|
|
|
|
g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE, "ImmediateAlertLevel");
|
|
}
|
|
|
|
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 gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
|
|
DBusMessageIter *iter, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
|
|
&monitor->linklosslevel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void property_set_link_loss_level(const GDBusPropertyTable *property,
|
|
DBusMessageIter *iter, GDBusPendingPropertySet id, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
const char *level;
|
|
|
|
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
|
|
g_dbus_pending_property_error(id,
|
|
ERROR_INTERFACE ".InvalidArguments",
|
|
"Invalid arguments in method call");
|
|
return;
|
|
}
|
|
|
|
dbus_message_iter_get_basic(iter, &level);
|
|
|
|
if (!level_is_valid(level)) {
|
|
g_dbus_pending_property_error(id,
|
|
ERROR_INTERFACE ".InvalidArguments",
|
|
"Invalid arguments in method call");
|
|
return;
|
|
}
|
|
|
|
if (g_strcmp0(monitor->linklosslevel, level) == 0)
|
|
goto done;
|
|
|
|
g_free(monitor->linklosslevel);
|
|
monitor->linklosslevel = g_strdup(level);
|
|
|
|
write_proximity_config(monitor->device, "LinkLossAlertLevel", level);
|
|
|
|
if (monitor->attrib)
|
|
write_alert_level(monitor);
|
|
|
|
done:
|
|
g_dbus_pending_property_success(id);
|
|
}
|
|
|
|
static gboolean property_exists_link_loss_level(
|
|
const GDBusPropertyTable *property, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
if (!monitor->enabled.linkloss)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean property_get_immediate_alert_level(
|
|
const GDBusPropertyTable *property,
|
|
DBusMessageIter *iter, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
|
|
&monitor->immediatelevel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void property_set_immediate_alert_level(
|
|
const GDBusPropertyTable *property, DBusMessageIter *iter,
|
|
GDBusPendingPropertySet id, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
struct btd_device *device = monitor->device;
|
|
const char *level;
|
|
|
|
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
|
|
g_dbus_pending_property_error(id,
|
|
ERROR_INTERFACE ".InvalidArguments",
|
|
"Invalid arguments in method call");
|
|
return;
|
|
}
|
|
|
|
dbus_message_iter_get_basic(iter, &level);
|
|
|
|
if (!level_is_valid(level)) {
|
|
g_dbus_pending_property_error(id,
|
|
ERROR_INTERFACE ".InvalidArguments",
|
|
"Invalid arguments in method call");
|
|
return;
|
|
}
|
|
|
|
if (g_strcmp0(monitor->immediatelevel, level) == 0)
|
|
goto done;
|
|
|
|
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(device,
|
|
attio_connected_cb,
|
|
attio_disconnected_cb,
|
|
monitor);
|
|
else if (monitor->attrib)
|
|
write_immediate_alert(monitor);
|
|
|
|
done:
|
|
g_dbus_pending_property_success(id);
|
|
}
|
|
|
|
static gboolean property_exists_immediate_alert_level(
|
|
const GDBusPropertyTable *property, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
if (!(monitor->enabled.findme || monitor->enabled.pathloss))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean property_get_signal_level(
|
|
const GDBusPropertyTable *property,
|
|
DBusMessageIter *iter, void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
|
|
&monitor->signallevel);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean property_exists_signal_level(const GDBusPropertyTable *property,
|
|
void *data)
|
|
{
|
|
struct monitor *monitor = data;
|
|
|
|
if (!monitor->enabled.pathloss)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const GDBusPropertyTable monitor_device_properties[] = {
|
|
{ "LinkLossAlertLevel", "s", property_get_link_loss_level,
|
|
property_set_link_loss_level,
|
|
property_exists_link_loss_level },
|
|
{ "ImmediateAlertLevel", "s", property_get_immediate_alert_level,
|
|
property_set_immediate_alert_level,
|
|
property_exists_immediate_alert_level },
|
|
{ "SignalLevel", "s", property_get_signal_level, NULL,
|
|
property_exists_signal_level },
|
|
{ }
|
|
};
|
|
|
|
static void monitor_destroy(gpointer user_data)
|
|
{
|
|
struct monitor *monitor = user_data;
|
|
|
|
btd_device_unref(monitor->device);
|
|
g_free(monitor->linklosslevel);
|
|
g_free(monitor->immediatelevel);
|
|
g_free(monitor->signallevel);
|
|
g_free(monitor);
|
|
|
|
monitors = g_slist_remove(monitors, monitor);
|
|
}
|
|
|
|
static struct monitor *register_monitor(struct btd_device *device)
|
|
{
|
|
const char *path = device_get_path(device);
|
|
struct monitor *monitor;
|
|
char *level;
|
|
|
|
monitor = find_monitor(device);
|
|
if (monitor != NULL)
|
|
return monitor;
|
|
|
|
level = read_proximity_config(device, "LinkLossAlertLevel");
|
|
|
|
monitor = g_new0(struct monitor, 1);
|
|
monitor->device = btd_device_ref(device);
|
|
monitor->linklosslevel = (level ? : g_strdup("high"));
|
|
monitor->signallevel = g_strdup("unknown");
|
|
monitor->immediatelevel = g_strdup("none");
|
|
|
|
monitors = g_slist_append(monitors, monitor);
|
|
|
|
if (g_dbus_register_interface(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE,
|
|
NULL, NULL, monitor_device_properties,
|
|
monitor, monitor_destroy) == FALSE) {
|
|
error("D-Bus failed to register %s interface",
|
|
PROXIMITY_INTERFACE);
|
|
monitor_destroy(monitor);
|
|
return NULL;
|
|
}
|
|
|
|
DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
|
|
|
|
return monitor;
|
|
}
|
|
|
|
static void update_monitor(struct monitor *monitor)
|
|
{
|
|
if (monitor->txpower != NULL && monitor->immediate != NULL)
|
|
monitor->enabled.pathloss = TRUE;
|
|
else
|
|
monitor->enabled.pathloss = FALSE;
|
|
|
|
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)
|
|
return;
|
|
|
|
if (monitor->attioid != 0)
|
|
return;
|
|
|
|
monitor->attioid = btd_device_add_attio_callback(monitor->device,
|
|
attio_connected_cb,
|
|
attio_disconnected_cb,
|
|
monitor);
|
|
}
|
|
|
|
int monitor_register_linkloss(struct btd_device *device,
|
|
struct enabled *enabled,
|
|
struct gatt_primary *linkloss)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
if (!enabled->linkloss)
|
|
return 0;
|
|
|
|
monitor = register_monitor(device);
|
|
if (monitor == NULL)
|
|
return -1;
|
|
|
|
monitor->linkloss = g_new0(struct att_range, 1);
|
|
monitor->linkloss->start = linkloss->range.start;
|
|
monitor->linkloss->end = linkloss->range.end;
|
|
monitor->enabled.linkloss = TRUE;
|
|
|
|
update_monitor(monitor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int monitor_register_txpower(struct btd_device *device,
|
|
struct enabled *enabled,
|
|
struct gatt_primary *txpower)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
if (!enabled->pathloss)
|
|
return 0;
|
|
|
|
monitor = register_monitor(device);
|
|
if (monitor == NULL)
|
|
return -1;
|
|
|
|
monitor->txpower = g_new0(struct att_range, 1);
|
|
monitor->txpower->start = txpower->range.start;
|
|
monitor->txpower->end = txpower->range.end;
|
|
|
|
update_monitor(monitor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int monitor_register_immediate(struct btd_device *device,
|
|
struct enabled *enabled,
|
|
struct gatt_primary *immediate)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
if (!enabled->pathloss && !enabled->findme)
|
|
return 0;
|
|
|
|
monitor = register_monitor(device);
|
|
if (monitor == NULL)
|
|
return -1;
|
|
|
|
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;
|
|
|
|
update_monitor(monitor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cleanup_monitor(struct monitor *monitor)
|
|
{
|
|
struct btd_device *device = monitor->device;
|
|
const char *path = device_get_path(device);
|
|
|
|
if (monitor->immediate != NULL || monitor->txpower != NULL)
|
|
return;
|
|
|
|
if (monitor->immediateto != 0) {
|
|
g_source_remove(monitor->immediateto);
|
|
monitor->immediateto = 0;
|
|
}
|
|
|
|
if (monitor->linkloss != NULL)
|
|
return;
|
|
|
|
if (monitor->attioid != 0) {
|
|
btd_device_remove_attio_callback(device, monitor->attioid);
|
|
monitor->attioid = 0;
|
|
}
|
|
|
|
if (monitor->attrib != NULL) {
|
|
g_attrib_unref(monitor->attrib);
|
|
monitor->attrib = NULL;
|
|
}
|
|
|
|
g_dbus_unregister_interface(btd_get_dbus_connection(), path,
|
|
PROXIMITY_INTERFACE);
|
|
}
|
|
|
|
void monitor_unregister_linkloss(struct btd_device *device)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
monitor = find_monitor(device);
|
|
if (monitor == NULL)
|
|
return;
|
|
|
|
g_free(monitor->linkloss);
|
|
monitor->linkloss = NULL;
|
|
monitor->enabled.linkloss = FALSE;
|
|
|
|
cleanup_monitor(monitor);
|
|
}
|
|
|
|
void monitor_unregister_txpower(struct btd_device *device)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
monitor = find_monitor(device);
|
|
if (monitor == NULL)
|
|
return;
|
|
|
|
g_free(monitor->txpower);
|
|
monitor->txpower = NULL;
|
|
monitor->enabled.pathloss = FALSE;
|
|
|
|
cleanup_monitor(monitor);
|
|
}
|
|
|
|
void monitor_unregister_immediate(struct btd_device *device)
|
|
{
|
|
struct monitor *monitor;
|
|
|
|
monitor = find_monitor(device);
|
|
if (monitor == NULL)
|
|
return;
|
|
|
|
g_free(monitor->immediate);
|
|
monitor->immediate = NULL;
|
|
monitor->enabled.findme = FALSE;
|
|
monitor->enabled.pathloss = FALSE;
|
|
|
|
cleanup_monitor(monitor);
|
|
}
|