3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2011 Nokia Corporation
6 * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #include <bluetooth/bluetooth.h>
38 #include <bluetooth/uuid.h>
40 #include "dbus-common.h"
52 #define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor"
54 #define ALERT_LEVEL_CHR_UUID 0x2A06
55 #define POWER_LEVEL_CHR_UUID 0x2A07
57 #define IMMEDIATE_TIMEOUT 5
66 struct btd_device *device;
69 struct att_range *linkloss;
70 struct att_range *txpower;
71 struct att_range *immediate;
72 struct enabled enabled;
73 char *linklosslevel; /* Link Loss Alert Level */
74 char *fallbacklevel; /* Immediate fallback alert level */
75 char *immediatelevel; /* Immediate Alert Level */
76 char *signallevel; /* Path Loss RSSI level */
77 uint16_t linklosshandle; /* Link Loss Characteristic
79 uint16_t txpowerhandle; /* Tx Characteristic Value Handle */
80 uint16_t immediatehandle; /* Immediate Alert Value Handle */
81 guint immediateto; /* Reset Immediate Alert to "none" */
85 static inline int create_filename(char *buf, size_t size,
86 const bdaddr_t *bdaddr, const char *name)
92 return create_name(buf, size, STORAGEDIR, addr, name);
95 static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
96 const char *alert, const char *level)
98 char filename[PATH_MAX + 1], addr[18], key[38];
100 create_filename(filename, PATH_MAX, sba, "proximity");
102 create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
106 snprintf(key, sizeof(key), "%17s#%s", addr, alert);
108 return textfile_put(filename, key, level);
111 static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
114 char filename[PATH_MAX + 1], addr[18], key[38];
117 create_filename(filename, PATH_MAX, sba, "proximity");
120 snprintf(key, sizeof(key), "%17s#%s", addr, alert);
122 str = textfile_caseget(filename, key);
126 strnew = g_strdup(str);
132 static uint8_t str2level(const char *level)
134 if (g_strcmp0("high", level) == 0)
136 else if (g_strcmp0("mild", level) == 0)
142 static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
145 struct monitor *monitor = user_data;
146 struct btd_device *device = monitor->device;
147 const char *path = device_get_path(device);
150 error("Link Loss Write Request failed: %s",
151 att_ecode2str(status));
155 if (!dec_write_resp(pdu, plen)) {
156 error("Link Loss Write Request: protocol error");
160 DBG("Link Loss Alert Level written");
162 emit_property_changed(monitor->conn, path,
163 PROXIMITY_INTERFACE, "LinkLossAlertLevel",
164 DBUS_TYPE_STRING, &monitor->linklosslevel);
167 static void char_discovered_cb(GSList *characteristics, guint8 status,
170 struct monitor *monitor = user_data;
171 struct gatt_char *chr;
172 uint8_t value = str2level(monitor->linklosslevel);
175 error("Discover Link Loss handle: %s", att_ecode2str(status));
179 DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
181 /* Assume there is a single Alert Level characteristic */
182 chr = characteristics->data;
183 monitor->linklosshandle = chr->value_handle;
185 gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
186 linkloss_written, monitor);
189 static int write_alert_level(struct monitor *monitor)
191 struct att_range *linkloss = monitor->linkloss;
194 if (monitor->linklosshandle) {
195 uint8_t value = str2level(monitor->linklosslevel);
197 gatt_write_char(monitor->attrib, monitor->linklosshandle,
198 &value, 1, linkloss_written, monitor);
202 bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
204 /* FIXME: use cache (requires service changed support) ? */
205 gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
206 &uuid, char_discovered_cb, monitor);
211 static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
214 uint8_t value[ATT_MAX_MTU];
218 DBG("Tx Power Level read failed: %s", att_ecode2str(status));
222 if (!dec_read_resp(pdu, plen, value, &vlen)) {
223 DBG("Protocol error");
228 DBG("Invalid length for TX Power value: %d", vlen);
232 DBG("Tx Power Level: %02x", (int8_t) value[0]);
235 static void tx_power_handle_cb(GSList *characteristics, guint8 status,
238 struct monitor *monitor = user_data;
239 struct gatt_char *chr;
242 error("Discover Tx Power handle: %s", att_ecode2str(status));
246 chr = characteristics->data;
247 monitor->txpowerhandle = chr->value_handle;
249 DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
251 gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
252 tx_power_read_cb, monitor);
255 static void read_tx_power(struct monitor *monitor)
257 struct att_range *txpower = monitor->txpower;
260 if (monitor->txpowerhandle != 0) {
261 gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
262 tx_power_read_cb, monitor);
266 bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
268 gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
269 &uuid, tx_power_handle_cb, monitor);
272 static gboolean immediate_timeout(gpointer user_data)
274 struct monitor *monitor = user_data;
275 const char *path = device_get_path(monitor->device);
277 monitor->immediateto = 0;
279 if (g_strcmp0(monitor->immediatelevel, "none") == 0)
282 if (monitor->attrib) {
283 uint8_t value = ALERT_NONE;
284 gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
285 &value, 1, NULL, NULL);
288 g_free(monitor->immediatelevel);
289 monitor->immediatelevel = g_strdup("none");
290 emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
291 "ImmediateAlertLevel", DBUS_TYPE_STRING,
292 &monitor->immediatelevel);
297 static void immediate_written(gpointer user_data)
299 struct monitor *monitor = user_data;
300 const char *path = device_get_path(monitor->device);
302 g_free(monitor->fallbacklevel);
303 monitor->fallbacklevel = NULL;
305 emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
306 "ImmediateAlertLevel",
307 DBUS_TYPE_STRING, &monitor->immediatelevel);
309 monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
310 immediate_timeout, monitor);
313 static void write_immediate_alert(struct monitor *monitor)
315 uint8_t value = str2level(monitor->immediatelevel);
317 gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
318 immediate_written, monitor);
321 static void immediate_handle_cb(GSList *characteristics, guint8 status,
324 struct monitor *monitor = user_data;
325 struct gatt_char *chr;
328 error("Discover Immediate Alert handle: %s",
329 att_ecode2str(status));
333 chr = characteristics->data;
334 monitor->immediatehandle = chr->value_handle;
336 DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
338 if (monitor->fallbacklevel)
339 write_immediate_alert(monitor);
342 static void discover_immediate_handle(struct monitor *monitor)
344 struct att_range *immediate = monitor->immediate;
347 bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
349 gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
350 &uuid, immediate_handle_cb, monitor);
353 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
355 struct monitor *monitor = user_data;
357 monitor->attrib = g_attrib_ref(attrib);
359 if (monitor->enabled.linkloss)
360 write_alert_level(monitor);
362 if (monitor->enabled.pathloss)
363 read_tx_power(monitor);
365 if (monitor->immediatehandle == 0) {
366 if(monitor->enabled.pathloss || monitor->enabled.findme)
367 discover_immediate_handle(monitor);
368 } else if (monitor->fallbacklevel)
369 write_immediate_alert(monitor);
372 static void attio_disconnected_cb(gpointer user_data)
374 struct monitor *monitor = user_data;
375 const char *path = device_get_path(monitor->device);
377 g_attrib_unref(monitor->attrib);
378 monitor->attrib = NULL;
380 if (monitor->immediateto == 0)
383 g_source_remove(monitor->immediateto);
384 monitor->immediateto = 0;
386 if (g_strcmp0(monitor->immediatelevel, "none") == 0)
389 g_free(monitor->immediatelevel);
390 monitor->immediatelevel = g_strdup("none");
391 emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
392 "ImmediateAlertLevel", DBUS_TYPE_STRING,
393 &monitor->immediatelevel);
396 static gboolean level_is_valid(const char *level)
398 return (g_str_equal("none", level) ||
399 g_str_equal("mild", level) ||
400 g_str_equal("high", level));
403 static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
404 const char *level, void *data)
406 struct monitor *monitor = data;
407 struct btd_device *device = monitor->device;
410 if (!level_is_valid(level))
411 return btd_error_invalid_args(msg);
413 if (g_strcmp0(monitor->linklosslevel, level) == 0)
414 return dbus_message_new_method_return(msg);
416 g_free(monitor->linklosslevel);
417 monitor->linklosslevel = g_strdup(level);
419 adapter_get_address(device_get_adapter(device), &sba);
420 device_get_address(device, &dba, NULL);
422 write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
425 write_alert_level(monitor);
427 return dbus_message_new_method_return(msg);
430 static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
431 const char *level, void *data)
433 struct monitor *monitor = data;
435 if (!level_is_valid(level))
436 return btd_error_invalid_args(msg);
438 if (g_strcmp0(monitor->immediatelevel, level) == 0)
439 return dbus_message_new_method_return(msg);
441 if (monitor->immediateto) {
442 g_source_remove(monitor->immediateto);
443 monitor->immediateto = 0;
446 /* Previous Immediate Alert level if connection/write fails */
447 g_free(monitor->fallbacklevel);
448 monitor->fallbacklevel = monitor->immediatelevel;
450 monitor->immediatelevel = g_strdup(level);
453 * Means that Link/Path Loss are disabled or there is a pending
454 * writting for Find Me(Immediate Alert characteristic value).
455 * If enabled, Path Loss always registers a connection callback
456 * when the Proximity Monitor starts.
458 if (monitor->attioid == 0)
459 monitor->attioid = btd_device_add_attio_callback(monitor->device,
461 attio_disconnected_cb,
463 else if (monitor->attrib)
464 write_immediate_alert(monitor);
466 return dbus_message_new_method_return(msg);
469 static DBusMessage *get_properties(DBusConnection *conn,
470 DBusMessage *msg, void *data)
472 struct monitor *monitor = data;
473 DBusMessageIter iter;
474 DBusMessageIter dict;
477 reply = dbus_message_new_method_return(msg);
481 dbus_message_iter_init_append(reply, &iter);
483 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
484 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
485 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
486 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
488 if (monitor->enabled.linkloss)
489 dict_append_entry(&dict, "LinkLossAlertLevel",
490 DBUS_TYPE_STRING, &monitor->linklosslevel);
492 if (monitor->enabled.findme || monitor->enabled.pathloss)
493 dict_append_entry(&dict, "ImmediateAlertLevel",
494 DBUS_TYPE_STRING, &monitor->immediatelevel);
496 if (monitor->enabled.pathloss)
497 dict_append_entry(&dict, "SignalLevel",
498 DBUS_TYPE_STRING, &monitor->signallevel);
500 dbus_message_iter_close_container(&iter, &dict);
505 static DBusMessage *set_property(DBusConnection *conn,
506 DBusMessage *msg, void *data)
508 struct monitor *monitor = data;
509 const char *property;
510 DBusMessageIter iter;
514 if (!dbus_message_iter_init(msg, &iter))
515 return btd_error_invalid_args(msg);
517 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
518 return btd_error_invalid_args(msg);
520 dbus_message_iter_get_basic(&iter, &property);
521 dbus_message_iter_next(&iter);
523 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
524 return btd_error_invalid_args(msg);
526 dbus_message_iter_recurse(&iter, &sub);
528 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
529 return btd_error_invalid_args(msg);
531 dbus_message_iter_get_basic(&sub, &level);
533 if (g_str_equal("ImmediateAlertLevel", property)) {
534 if (monitor->enabled.findme == FALSE &&
535 monitor->enabled.pathloss == FALSE)
536 return btd_error_not_available(msg);
538 return set_immediate_alert(conn, msg, level, data);
539 } else if (g_str_equal("LinkLossAlertLevel", property)) {
540 if (monitor->enabled.linkloss == FALSE)
541 return btd_error_not_available(msg);
543 return set_link_loss_alert(conn, msg, level, data);
546 return btd_error_invalid_args(msg);
549 static const GDBusMethodTable monitor_methods[] = {
550 { GDBUS_METHOD("GetProperties",
551 NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
553 { GDBUS_ASYNC_METHOD("SetProperty",
554 GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
559 static const GDBusSignalTable monitor_signals[] = {
560 { GDBUS_SIGNAL("PropertyChanged",
561 GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
565 static void monitor_destroy(gpointer user_data)
567 struct monitor *monitor = user_data;
569 if (monitor->immediateto)
570 g_source_remove(monitor->immediateto);
572 if (monitor->attioid)
573 btd_device_remove_attio_callback(monitor->device,
576 g_attrib_unref(monitor->attrib);
578 dbus_connection_unref(monitor->conn);
579 btd_device_unref(monitor->device);
580 g_free(monitor->linkloss);
581 g_free(monitor->immediate);
582 g_free(monitor->txpower);
583 g_free(monitor->linklosslevel);
584 g_free(monitor->immediatelevel);
585 g_free(monitor->signallevel);
589 int monitor_register(DBusConnection *conn, struct btd_device *device,
590 struct gatt_primary *linkloss, struct gatt_primary *txpower,
591 struct gatt_primary *immediate, struct enabled *enabled)
593 const char *path = device_get_path(device);
594 struct monitor *monitor;
598 adapter_get_address(device_get_adapter(device), &sba);
599 device_get_address(device, &dba, NULL);
601 level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
603 monitor = g_new0(struct monitor, 1);
604 monitor->device = btd_device_ref(device);
605 monitor->conn = dbus_connection_ref(conn);
606 monitor->linklosslevel = (level ? : g_strdup("high"));
607 monitor->signallevel = g_strdup("unknown");
608 monitor->immediatelevel = g_strdup("none");
610 if (g_dbus_register_interface(conn, path,
612 monitor_methods, monitor_signals,
613 NULL, monitor, monitor_destroy) == FALSE) {
614 error("D-Bus failed to register %s interface",
615 PROXIMITY_INTERFACE);
616 monitor_destroy(monitor);
620 DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
622 if (linkloss && enabled->linkloss) {
623 monitor->linkloss = g_new0(struct att_range, 1);
624 monitor->linkloss->start = linkloss->range.start;
625 monitor->linkloss->end = linkloss->range.end;
627 monitor->enabled.linkloss = TRUE;
631 if (txpower && enabled->pathloss) {
632 monitor->txpower = g_new0(struct att_range, 1);
633 monitor->txpower->start = txpower->range.start;
634 monitor->txpower->end = txpower->range.end;
636 monitor->enabled.pathloss = TRUE;
639 if (enabled->pathloss || enabled->findme) {
640 monitor->immediate = g_new0(struct att_range, 1);
641 monitor->immediate->start = immediate->range.start;
642 monitor->immediate->end = immediate->range.end;
645 monitor->enabled.findme = enabled->findme;
648 DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
649 monitor->enabled.linkloss ? "TRUE" : "FALSE",
650 monitor->enabled.pathloss ? "TRUE" : "FALSE",
651 monitor->enabled.findme ? "TRUE" : "FALSE");
653 if (monitor->enabled.linkloss || monitor->enabled.pathloss)
654 monitor->attioid = btd_device_add_attio_callback(device,
656 attio_disconnected_cb,
662 void monitor_unregister(DBusConnection *conn, struct btd_device *device)
664 const char *path = device_get_path(device);
666 g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);