Merge BlueZ 5.43 to master
Release of BlueZ 5.43
29th October 2016, 09:44 am by Johan Hedberg
This is almost purely a bug-fix release with fixes to HoG, ATT and PAN.
There’s also a fix for a regression in 5.42 that caused connection failures
for external profiles like OBEX. Feature-wise there’s one notable addition:
LE privacy. By enabling this in main.conf it’s now possible to make BlueZ
generate a local Identity Resolving Key (IRK) and use Resolvable Private
Addresses (RPAs) for itself.
Release of BlueZ 5.42
26th September 2016, 06:33 pm by Johan Hedberg
The major API change with this release is that the GATT D-Bus API is no longer
marked as experimental. This should hopefully encourage the creation of more
applications using it. Feature-wise, btmon received support for decoding full
mgmt message traces – a feature that is available in the bluetooth-next kernel
tree, i.e. on its way to the 4.9 kernel release. In addition to these changes,
this release contains also fixes in areas such as PBAP, transport selection
(BR/EDR vs LE), ATT and A2DP.
NOTE:
Adjustments were necessary to audioOverBle, battery, and oad profiles due to:
commit 82666b345e14b0a723e3d7cf8e56003edef22fed
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Mon Sep 26 16:46:09 2016 +0300
core: Remove attio callbacks
Change-Id: I8d25c7c866f76c23fcca284e3b20b237b4b1b7eb
diff --git a/AUTHORS b/AUTHORS
index 16fb14c..176b55b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -99,3 +99,4 @@
Bharat Panda <bharat.panda@samsung.com>
Marie Janssen <jamuraa@chromium.org>
Jaganath Kanakkassery <jaganath.k@samsung.com>
+Michał Narajowski <michal.narajowski@codecoup.pl>
diff --git a/ChangeLog b/ChangeLog
index 920175e..8e01791 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+ver 5.43:
+ Fix issue with HID over GATT support.
+ Fix issue with ATT Find By Type response handling.
+ Fix issue with handling insufficient authentication.
+ Fix issue with bonding while pairing is in progress.
+ Fix issue with BR/EDR pairing for dual-mode devices.
+ Fix issue with handling profile policy resets.
+ Fix issue with connecting state of services.
+ Fix issue with handling PAN GN Master role.
+ Add support for enabling LE Privacy feature.
+
+ver 5.42:
+ Fix issue with PBAP call logs from different folders.
+ Fix issue with OBEX over L2CAP and PowerPC architecture.
+ Fix issue with BR/EDR over LE selection during discovery.
+ Fix issue with selection of bearer after bonding.
+ Fix issue with handling socket recv() return values.
+ Fix issue with setting connecting service state.
+ Fix issue with setting correct ATT default MTU value.
+ Fix issue with not setting AVRCP player identifier.
+ Fix issue with handling AVRCP browsable player.
+ Fix issue with addressing AVRCP player changes.
+ Add support for new management tracing capability.
+ Mark GATT D-Bus APIs as stable interfaces.
+
ver 5.41:
Fix issue with service state changes handling.
Fix issue with AVRCP and no available player.
diff --git a/Makefile.am b/Makefile.am
index 4ce642f..c469a6c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:13:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:14:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
@@ -185,7 +185,7 @@
src/profile.h src/profile.c \
src/service.h src/service.c \
src/gatt-client.h src/gatt-client.c \
- src/device.h src/device.c src/attio.h \
+ src/device.h src/device.c \
src/dbus-common.c src/dbus-common.h \
src/eir.h src/eir.c
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
diff --git a/Makefile.plugins b/Makefile.plugins
index 78530ec..192a2cc 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -96,35 +96,6 @@
builtin_modules += deviceinfo
builtin_sources += profiles/deviceinfo/deviceinfo.c
-if DEPRECATED
-builtin_modules += alert
-builtin_sources += profiles/alert/server.c
-
-builtin_modules += time
-builtin_sources += profiles/time/server.c
-
-builtin_modules += proximity
-builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
- profiles/proximity/manager.c \
- profiles/proximity/monitor.h \
- profiles/proximity/monitor.c \
- profiles/proximity/reporter.h \
- profiles/proximity/reporter.c \
- profiles/proximity/linkloss.h \
- profiles/proximity/linkloss.c \
- profiles/proximity/immalert.h \
- profiles/proximity/immalert.c
-
-builtin_modules += thermometer
-builtin_sources += profiles/thermometer/thermometer.c
-
-builtin_modules += heartrate
-builtin_sources += profiles/heartrate/heartrate.c
-
-builtin_modules += cyclingspeed
-builtin_sources += profiles/cyclingspeed/cyclingspeed.c
-endif
-
if SIXAXIS
plugin_LTLIBRARIES += plugins/sixaxis.la
plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
diff --git a/Makefile.tools b/Makefile.tools
index af709b8..7ed442c 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -5,6 +5,8 @@
client_bluetoothctl_SOURCES = client/main.c \
client/display.h client/display.c \
client/agent.h client/agent.c \
+ client/advertising.h \
+ client/advertising.c \
client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
diff --git a/client/advertising.c b/client/advertising.c
new file mode 100644
index 0000000..62201d5
--- /dev/null
+++ b/client/advertising.c
@@ -0,0 +1,452 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <readline/readline.h>
+#include <wordexp.h>
+
+#include "gdbus/gdbus.h"
+#include "display.h"
+#include "advertising.h"
+
+#define AD_PATH "/org/bluez/advertising"
+#define AD_IFACE "org.bluez.LEAdvertisement1"
+
+static gboolean registered = FALSE;
+static char *ad_type = NULL;
+static char **ad_uuids = NULL;
+static size_t ad_uuids_len = 0;
+static char *ad_service_uuid = NULL;
+static uint8_t ad_service_data[25];
+static uint8_t ad_service_data_len = 0;
+static uint16_t ad_manufacturer_id;
+static uint8_t ad_manufacturer_data[25];
+static uint8_t ad_manufacturer_data_len = 0;
+static gboolean ad_tx_power = FALSE;
+
+static void ad_release(DBusConnection *conn)
+{
+ registered = FALSE;
+
+ g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
+}
+
+static DBusMessage *release_advertising(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ rl_printf("Advertising released\n");
+
+ ad_release(conn);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable ad_methods[] = {
+ { GDBUS_METHOD("Release", NULL, NULL, release_advertising) },
+ { }
+};
+
+static void register_setup(DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+ 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);
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+static void register_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = TRUE;
+ rl_printf("Advertising object registered\n");
+ } else {
+ rl_printf("Failed to register advertisement: %s\n", error.name);
+ dbus_error_free(&error);
+
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ }
+}
+
+static gboolean get_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *type = "peripheral";
+
+ if (!ad_type || strlen(ad_type) > 0)
+ type = ad_type;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
+
+ return TRUE;
+}
+
+static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
+{
+ return ad_uuids_len != 0;
+}
+
+static gboolean get_uuids(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter array;
+ size_t i;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
+
+ for (i = 0; i < ad_uuids_len; i++)
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &ad_uuids[i]);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter variant, array;
+ char type_sig[2] = { type, '\0' };
+ char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+ array_sig, &variant);
+
+ dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+ type_sig, &array);
+
+ if (dbus_type_is_fixed(type) == TRUE) {
+ dbus_message_iter_append_fixed_array(&array, type, val,
+ n_elements);
+ } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+ const char ***str_array = val;
+ int i;
+
+ for (i = 0; i < n_elements; i++)
+ dbus_message_iter_append_basic(&array, type,
+ &((*str_array)[i]));
+ }
+
+ dbus_message_iter_close_container(&variant, &array);
+
+ dbus_message_iter_close_container(iter, &variant);
+}
+
+static void dict_append_basic_array(DBusMessageIter *dict, int key_type,
+ const void *key, int type, void *val,
+ int n_elements)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, key_type, key);
+
+ append_array_variant(&entry, type, val, n_elements);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+ void *val, int n_elements)
+{
+ dict_append_basic_array(dict, DBUS_TYPE_STRING, &key, type, val,
+ n_elements);
+}
+
+static gboolean service_data_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ return ad_service_uuid != NULL;
+}
+
+static gboolean get_service_data(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const uint8_t *data = ad_service_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+ dict_append_array(&dict, ad_service_uuid, DBUS_TYPE_BYTE, &data,
+ ad_service_data_len);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return TRUE;
+}
+
+static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ return ad_manufacturer_id != 0;
+}
+
+static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ DBusMessageIter dict;
+ const uint8_t *data = ad_manufacturer_data;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
+
+ dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad_manufacturer_id,
+ DBUS_TYPE_BYTE, &data,
+ ad_manufacturer_data_len);
+
+ dbus_message_iter_close_container(iter, &dict);
+
+ return TRUE;
+}
+
+static gboolean tx_power_exists(const GDBusPropertyTable *property, void *data)
+{
+ return ad_tx_power;
+}
+
+static gboolean get_tx_power(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &ad_tx_power);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable ad_props[] = {
+ { "Type", "s", get_type },
+ { "ServiceUUIDs", "as", get_uuids, NULL, uuids_exists },
+ { "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
+ { "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
+ manufacturer_data_exists },
+ { "IncludeTxPower", "b", get_tx_power, NULL, tx_power_exists },
+ { }
+};
+
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
+{
+ if (registered == TRUE) {
+ rl_printf("Advertisement is already registered\n");
+ return;
+ }
+
+ ad_type = g_strdup(type);
+
+ if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
+ NULL, ad_props, NULL, NULL) == FALSE) {
+ rl_printf("Failed to register advertising object\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
+ register_setup, register_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to register advertising\n");
+ return;
+ }
+}
+
+static void unregister_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = AD_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+static void unregister_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == FALSE) {
+ registered = FALSE;
+ rl_printf("Advertising object unregistered\n");
+ if (g_dbus_unregister_interface(conn, AD_PATH,
+ AD_IFACE) == FALSE)
+ rl_printf("Failed to unregister advertising object\n");
+ } else {
+ rl_printf("Failed to unregister advertisement: %s\n",
+ error.name);
+ dbus_error_free(&error);
+ }
+}
+
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
+{
+ if (!manager)
+ ad_release(conn);
+
+ if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
+ unregister_setup, unregister_reply,
+ conn, NULL) == FALSE) {
+ rl_printf("Failed to unregister advertisement method\n");
+ return;
+ }
+}
+
+void ad_advertise_uuids(const char *arg)
+{
+ g_strfreev(ad_uuids);
+ ad_uuids = NULL;
+ ad_uuids_len = 0;
+
+ if (!arg || !strlen(arg))
+ return;
+
+ ad_uuids = g_strsplit(arg, " ", -1);
+ if (!ad_uuids) {
+ rl_printf("Failed to parse input\n");
+ return;
+ }
+
+ ad_uuids_len = g_strv_length(ad_uuids);
+}
+
+static void ad_clear_service(void)
+{
+ g_free(ad_service_uuid);
+ ad_service_uuid = NULL;
+ memset(ad_service_data, 0, sizeof(ad_service_data));
+ ad_service_data_len = 0;
+}
+
+void ad_advertise_service(const char *arg)
+{
+ wordexp_t w;
+ unsigned int i;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_clear_service();
+
+ if (w.we_wordc == 0)
+ goto done;
+
+ ad_service_uuid = g_strdup(w.we_wordv[0]);
+
+ for (i = 1; i < w.we_wordc; i++) {
+ long int val;
+ char *endptr = NULL;
+
+ if (i >= G_N_ELEMENTS(ad_service_data)) {
+ rl_printf("Too much data\n");
+ goto done;
+ }
+
+ val = strtol(w.we_wordv[i], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ rl_printf("Invalid value at index %d\n", i);
+ ad_clear_service();
+ goto done;
+ }
+
+ ad_service_data[ad_service_data_len] = val;
+ ad_service_data_len++;
+ }
+
+done:
+ wordfree(&w);
+}
+
+static void ad_clear_manufacturer(void)
+{
+ ad_manufacturer_id = 0;
+ memset(ad_manufacturer_data, 0, sizeof(ad_manufacturer_data));
+ ad_manufacturer_data_len = 0;
+}
+
+void ad_advertise_manufacturer(const char *arg)
+{
+ wordexp_t w;
+ unsigned int i;
+ char *endptr = NULL;
+ long int val;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ ad_clear_manufacturer();
+
+ if (w.we_wordc == 0)
+ goto done;
+
+ val = strtol(w.we_wordv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
+ rl_printf("Invalid manufacture id\n");
+ goto done;
+ }
+
+ ad_manufacturer_id = val;
+
+ for (i = 1; i < w.we_wordc; i++) {
+ if (i >= G_N_ELEMENTS(ad_service_data)) {
+ rl_printf("Too much data\n");
+ goto done;
+ }
+
+ val = strtol(w.we_wordv[i], &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ rl_printf("Invalid value at index %d\n", i);
+ ad_clear_service();
+ goto done;
+ }
+
+ ad_manufacturer_data[ad_manufacturer_data_len] = val;
+ ad_manufacturer_data_len++;
+ }
+
+done:
+ wordfree(&w);
+}
+
+
+void ad_advertise_tx_power(gboolean value)
+{
+ ad_tx_power = value;
+}
diff --git a/src/attio.h b/client/advertising.h
similarity index 62%
rename from src/attio.h
rename to client/advertising.h
index 16e2873..8638465 100644
--- a/src/attio.h
+++ b/client/advertising.h
@@ -2,8 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2011 Nokia Corporation
- * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
@@ -22,12 +21,10 @@
*
*/
-typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
-typedef void (*attio_disconnect_cb) (gpointer user_data);
+void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type);
+void ad_unregister(DBusConnection *conn, GDBusProxy *manager);
-guint btd_device_add_attio_callback(struct btd_device *device,
- attio_connect_cb cfunc,
- attio_disconnect_cb dcfunc,
- gpointer user_data);
-
-gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
+void ad_advertise_uuids(const char *arg);
+void ad_advertise_service(const char *arg);
+void ad_advertise_manufacturer(const char *arg);
+void ad_advertise_tx_power(gboolean value);
diff --git a/client/gatt.c b/client/gatt.c
index fee1cf9..37f222d 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -73,15 +73,21 @@
text = uuidstr_to_str(uuid);
if (!text)
- text = uuid;
-
- rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
- description ? "[" : "",
- description ? : "",
- description ? "] " : "",
- primary ? "Primary" : "Secondary",
- g_dbus_proxy_get_path(proxy),
- text);
+ rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ primary ? "Primary" : "Secondary",
+ g_dbus_proxy_get_path(proxy),
+ uuid);
+ else
+ rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ primary ? "Primary" : "Secondary",
+ g_dbus_proxy_get_path(proxy),
+ uuid, text);
}
void gatt_add_service(GDBusProxy *proxy)
@@ -116,14 +122,19 @@
text = uuidstr_to_str(uuid);
if (!text)
- text = uuid;
-
- rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
- description ? "[" : "",
- description ? : "",
- description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- text);
+ rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ uuid);
+ else
+ rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ uuid, text);
}
static gboolean characteristic_is_child(GDBusProxy *characteristic)
@@ -184,14 +195,19 @@
text = uuidstr_to_str(uuid);
if (!text)
- text = uuid;
-
- rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
- description ? "[" : "",
- description ? : "",
- description ? "] " : "",
- g_dbus_proxy_get_path(proxy),
- text);
+ rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ uuid);
+ else
+ rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ uuid, text);
}
static gboolean descriptor_is_child(GDBusProxy *characteristic)
diff --git a/client/main.c b/client/main.c
index 32341ad..e1198a8 100644
--- a/client/main.c
+++ b/client/main.c
@@ -38,11 +38,13 @@
#include <readline/history.h>
#include <glib.h>
+#include "src/shared/util.h"
#include "gdbus/gdbus.h"
#include "monitor/uuid.h"
#include "agent.h"
#include "display.h"
#include "gatt.h"
+#include "advertising.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
@@ -58,11 +60,16 @@
static GDBusProxy *agent_manager;
static char *auto_register_agent = NULL;
-static GDBusProxy *default_ctrl;
+struct adapter {
+ GDBusProxy *proxy;
+ GList *devices;
+};
+
+static struct adapter *default_ctrl;
static GDBusProxy *default_dev;
static GDBusProxy *default_attr;
+static GDBusProxy *ad_manager;
static GList *ctrl_list;
-static GList *dev_list;
static guint input = 0;
@@ -77,6 +84,14 @@
NULL
};
+static const char * const ad_arguments[] = {
+ "on",
+ "off",
+ "peripheral",
+ "broadcast",
+ NULL
+};
+
static void proxy_leak(gpointer data)
{
printf("Leaking proxy %p\n", data);
@@ -120,9 +135,6 @@
printf("\r");
rl_on_new_line();
rl_redisplay();
-
- if (!input)
- input = setup_standard_input();
}
static void disconnect_handler(DBusConnection *connection, void *user_data)
@@ -137,13 +149,10 @@
rl_on_new_line();
rl_redisplay();
- g_list_free(ctrl_list);
+ g_list_free_full(ctrl_list, proxy_leak);
ctrl_list = NULL;
default_ctrl = NULL;
-
- g_list_free(dev_list);
- dev_list = NULL;
}
static void print_adapter(GDBusProxy *proxy, const char *description)
@@ -166,7 +175,9 @@
description ? : "",
description ? "] " : "",
address, name,
- default_ctrl == proxy ? "[default]" : "");
+ default_ctrl &&
+ default_ctrl->proxy == proxy ?
+ "[default]" : "");
}
@@ -349,8 +360,11 @@
dbus_message_iter_get_basic(&iter, &device);
- for (l = dev_list; l; l = g_list_next(l)) {
- GDBusProxy *proxy = l->data;
+ if (!default_ctrl)
+ return FALSE;
+
+ for (l = default_ctrl->devices; l; l = g_list_next(l)) {
+ struct GDBusProxy *proxy = l->data;
path = g_dbus_proxy_get_path(proxy);
@@ -361,6 +375,19 @@
return FALSE;
}
+static struct adapter *find_parent(GDBusProxy *device)
+{
+ GList *list;
+
+ for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+
+ if (device_is_child(device, adapter->proxy) == TRUE)
+ return adapter;
+ }
+ return NULL;
+}
+
static void set_default_device(GDBusProxy *proxy, const char *attribute)
{
char *desc = NULL;
@@ -396,9 +423,14 @@
static void device_added(GDBusProxy *proxy)
{
DBusMessageIter iter;
+ struct adapter *adapter = find_parent(proxy);
- dev_list = g_list_append(dev_list, proxy);
+ if (!adapter) {
+ /* TODO: Error */
+ return;
+ }
+ adapter->devices = g_list_append(adapter->devices, proxy);
print_device(proxy, COLORED_NEW);
if (default_dev)
@@ -414,6 +446,19 @@
}
}
+static void adapter_added(GDBusProxy *proxy)
+{
+ struct adapter *adapter = g_malloc0(sizeof(struct adapter));
+
+ adapter->proxy = proxy;
+ ctrl_list = g_list_append(ctrl_list, adapter);
+
+ if (!default_ctrl)
+ default_ctrl = adapter;
+
+ print_adapter(proxy, COLORED_NEW);
+}
+
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -421,16 +466,9 @@
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, "org.bluez.Device1")) {
- if (device_is_child(proxy, default_ctrl) == TRUE)
- device_added(proxy);
-
+ device_added(proxy);
} else if (!strcmp(interface, "org.bluez.Adapter1")) {
- ctrl_list = g_list_append(ctrl_list, proxy);
-
- if (!default_ctrl)
- default_ctrl = proxy;
-
- print_adapter(proxy, COLORED_NEW);
+ adapter_added(proxy);
} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
if (!agent_manager) {
agent_manager = proxy;
@@ -448,6 +486,8 @@
gatt_add_descriptor(proxy);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_add_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ ad_manager = proxy;
}
}
@@ -462,6 +502,46 @@
set_default_device(default_dev, path);
}
+static void device_removed(GDBusProxy *proxy)
+{
+ struct adapter *adapter = find_parent(proxy);
+ if (!adapter) {
+ /* TODO: Error */
+ return;
+ }
+
+ adapter->devices = g_list_remove(adapter->devices, proxy);
+
+ print_device(proxy, COLORED_DEL);
+
+ if (default_dev == proxy)
+ set_default_device(NULL, NULL);
+}
+
+static void adapter_removed(GDBusProxy *proxy)
+{
+ GList *ll;
+
+ for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) {
+ struct adapter *adapter = ll->data;
+
+ if (adapter->proxy == proxy) {
+ print_adapter(proxy, COLORED_DEL);
+
+ if (default_ctrl && default_ctrl->proxy == proxy) {
+ default_ctrl = NULL;
+ set_default_device(NULL, NULL);
+ }
+
+ ctrl_list = g_list_remove_link(ctrl_list, ll);
+ g_list_free(adapter->devices);
+ g_free(adapter);
+ g_list_free(ll);
+ return;
+ }
+ }
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
@@ -469,26 +549,9 @@
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, "org.bluez.Device1")) {
- if (device_is_child(proxy, default_ctrl) == TRUE) {
- dev_list = g_list_remove(dev_list, proxy);
-
- print_device(proxy, COLORED_DEL);
-
- if (default_dev == proxy)
- set_default_device(NULL, NULL);
- }
+ device_removed(proxy);
} else if (!strcmp(interface, "org.bluez.Adapter1")) {
- ctrl_list = g_list_remove(ctrl_list, proxy);
-
- print_adapter(proxy, COLORED_DEL);
-
- if (default_ctrl == proxy) {
- default_ctrl = NULL;
- set_default_device(NULL, NULL);
-
- g_list_free(dev_list);
- dev_list = NULL;
- }
+ adapter_removed(proxy);
} else if (!strcmp(interface, "org.bluez.AgentManager1")) {
if (agent_manager == proxy) {
agent_manager = NULL;
@@ -512,6 +575,11 @@
set_default_attribute(NULL);
} else if (!strcmp(interface, "org.bluez.GattManager1")) {
gatt_remove_manager(proxy);
+ } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+ if (ad_manager == proxy) {
+ agent_manager = NULL;
+ ad_unregister(dbus_conn, NULL);
+ }
}
}
@@ -523,7 +591,8 @@
interface = g_dbus_proxy_get_interface(proxy);
if (!strcmp(interface, "org.bluez.Device1")) {
- if (device_is_child(proxy, default_ctrl) == TRUE) {
+ if (default_ctrl && device_is_child(proxy,
+ default_ctrl->proxy) == TRUE) {
DBusMessageIter addr_iter;
char *str;
@@ -586,6 +655,28 @@
dbus_message_get_member(message));
}
+static struct adapter *find_ctrl_by_address(GList *source, const char *address)
+{
+ GList *list;
+
+ for (list = g_list_first(source); list; list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+ DBusMessageIter iter;
+ const char *str;
+
+ if (g_dbus_proxy_get_property(adapter->proxy,
+ "Address", &iter) == FALSE)
+ continue;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (!strcmp(str, address))
+ return adapter;
+ }
+
+ return NULL;
+}
+
static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
{
GList *list;
@@ -676,13 +767,14 @@
GList *list;
for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
- GDBusProxy *proxy = list->data;
- print_adapter(proxy, NULL);
+ struct adapter *adapter = list->data;
+ print_adapter(adapter->proxy, NULL);
}
}
static void cmd_show(const char *arg)
{
+ struct adapter *adapter;
GDBusProxy *proxy;
DBusMessageIter iter;
const char *address;
@@ -691,13 +783,14 @@
if (check_default_ctrl() == FALSE)
return;
- proxy = default_ctrl;
+ proxy = default_ctrl->proxy;
} else {
- proxy = find_proxy_by_address(ctrl_list, arg);
- if (!proxy) {
+ adapter = find_ctrl_by_address(ctrl_list, arg);
+ if (!adapter) {
rl_printf("Controller %s not available\n", arg);
return;
}
+ proxy = adapter->proxy;
}
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
@@ -719,45 +812,50 @@
static void cmd_select(const char *arg)
{
- GDBusProxy *proxy;
+ struct adapter *adapter;
if (!arg || !strlen(arg)) {
rl_printf("Missing controller address argument\n");
return;
}
- proxy = find_proxy_by_address(ctrl_list, arg);
- if (!proxy) {
+ adapter = find_ctrl_by_address(ctrl_list, arg);
+ if (!adapter) {
rl_printf("Controller %s not available\n", arg);
return;
}
- if (default_ctrl == proxy)
+ if (default_ctrl && default_ctrl->proxy == adapter->proxy)
return;
- default_ctrl = proxy;
- print_adapter(proxy, NULL);
-
- g_list_free(dev_list);
- dev_list = NULL;
+ default_ctrl = adapter;
+ print_adapter(adapter->proxy, NULL);
}
static void cmd_devices(const char *arg)
{
- GList *list;
+ GList *ll;
- for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
- GDBusProxy *proxy = list->data;
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ for (ll = g_list_first(default_ctrl->devices);
+ ll; ll = g_list_next(ll)) {
+ GDBusProxy *proxy = ll->data;
print_device(proxy, NULL);
}
}
static void cmd_paired_devices(const char *arg)
{
- GList *list;
+ GList *ll;
- for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
- GDBusProxy *proxy = list->data;
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ for (ll = g_list_first(default_ctrl->devices);
+ ll; ll = g_list_next(ll)) {
+ GDBusProxy *proxy = ll->data;
DBusMessageIter iter;
dbus_bool_t paired;
@@ -796,7 +894,7 @@
name = g_strdup(arg);
- if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
DBUS_TYPE_STRING, &name,
generic_callback, name, g_free) == TRUE)
return;
@@ -813,7 +911,7 @@
name = g_strdup("");
- if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias",
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
DBUS_TYPE_STRING, &name,
generic_callback, name, g_free) == TRUE)
return;
@@ -834,7 +932,7 @@
str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off");
- if (g_dbus_proxy_set_property_basic(default_ctrl, "Powered",
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered",
DBUS_TYPE_BOOLEAN, &powered,
generic_callback, str, g_free) == TRUE)
return;
@@ -855,7 +953,7 @@
str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off");
- if (g_dbus_proxy_set_property_basic(default_ctrl, "Pairable",
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Pairable",
DBUS_TYPE_BOOLEAN, &pairable,
generic_callback, str, g_free) == TRUE)
return;
@@ -877,7 +975,7 @@
str = g_strdup_printf("discoverable %s",
discoverable == TRUE ? "on" : "off");
- if (g_dbus_proxy_set_property_basic(default_ctrl, "Discoverable",
+ if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Discoverable",
DBUS_TYPE_BOOLEAN, &discoverable,
generic_callback, str, g_free) == TRUE)
return;
@@ -951,7 +1049,7 @@
else
method = "StopDiscovery";
- if (g_dbus_proxy_method_call(default_ctrl, method,
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
NULL, start_discovery_reply,
GUINT_TO_POINTER(enable), NULL) == FALSE) {
rl_printf("Failed to %s discovery\n",
@@ -1120,7 +1218,7 @@
if (check_default_ctrl() == FALSE)
return;
- if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
set_discovery_filter_setup, set_discovery_filter_reply,
&args, NULL) == FALSE) {
rl_printf("Failed to set discovery filter\n");
@@ -1209,7 +1307,10 @@
g_free(filtered_scan_transport);
filtered_scan_transport = NULL;
- if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter",
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
clear_discovery_filter_setup, set_discovery_filter_reply,
NULL, NULL) == FALSE) {
rl_printf("Failed to clear discovery filter\n");
@@ -1227,7 +1328,10 @@
return NULL;
}
- proxy = find_proxy_by_address(dev_list, arg);
+ if (check_default_ctrl() == FALSE)
+ return NULL;
+
+ proxy = find_proxy_by_address(default_ctrl->devices, arg);
if (!proxy) {
rl_printf("Device %s not available\n", arg);
return NULL;
@@ -1418,7 +1522,10 @@
path = g_strdup(g_dbus_proxy_get_path(proxy));
- if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice",
+ if (!default_ctrl)
+ return;
+
+ if (g_dbus_proxy_method_call(default_ctrl->proxy, "RemoveDevice",
remove_device_setup,
remove_device_reply,
path, g_free) == FALSE) {
@@ -1442,16 +1549,16 @@
if (strcmp(arg, "*") == 0) {
GList *list;
- for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+ for (list = default_ctrl->devices; list;
+ list = g_list_next(list)) {
GDBusProxy *proxy = list->data;
remove_device(proxy);
}
-
return;
}
- proxy = find_proxy_by_address(dev_list, arg);
+ proxy = find_proxy_by_address(default_ctrl->devices, arg);
if (!proxy) {
rl_printf("Device %s not available\n", arg);
return;
@@ -1487,7 +1594,10 @@
return;
}
- proxy = find_proxy_by_address(dev_list, arg);
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ proxy = find_proxy_by_address(default_ctrl->devices, arg);
if (!proxy) {
rl_printf("Device %s not available\n", arg);
return;
@@ -1556,6 +1666,30 @@
gatt_list_attributes(g_dbus_proxy_get_path(proxy));
}
+static void cmd_set_alias(const char *arg)
+{
+ char *name;
+
+ if (!arg || !strlen(arg)) {
+ rl_printf("Missing name argument\n");
+ return;
+ }
+
+ if (!default_dev) {
+ rl_printf("No device connected\n");
+ return;
+ }
+
+ name = g_strdup(arg);
+
+ if (g_dbus_proxy_set_property_basic(default_dev, "Alias",
+ DBUS_TYPE_STRING, &name,
+ generic_callback, name, g_free) == TRUE)
+ return;
+
+ g_free(name);
+}
+
static void cmd_select_attribute(const char *arg)
{
GDBusProxy *proxy;
@@ -1697,7 +1831,7 @@
return;
}
- gatt_register_profile(dbus_conn, default_ctrl, &w);
+ gatt_register_profile(dbus_conn, default_ctrl->proxy, &w);
wordfree(&w);
}
@@ -1707,7 +1841,7 @@
if (check_default_ctrl() == FALSE)
return;
- gatt_unregister_profile(dbus_conn, default_ctrl);
+ gatt_unregister_profile(dbus_conn, default_ctrl->proxy);
}
static void cmd_version(const char *arg)
@@ -1753,12 +1887,40 @@
static char *ctrl_generator(const char *text, int state)
{
- return generic_generator(text, state, ctrl_list, "Address");
+ static int index = 0;
+ static int len = 0;
+ GList *list;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ for (list = g_list_nth(ctrl_list, index); list;
+ list = g_list_next(list)) {
+ struct adapter *adapter = list->data;
+ DBusMessageIter iter;
+ const char *str;
+
+ index++;
+
+ if (g_dbus_proxy_get_property(adapter->proxy,
+ "Address", &iter) == FALSE)
+ continue;
+
+ dbus_message_iter_get_basic(&iter, &str);
+
+ if (!strncmp(str, text, len))
+ return strdup(str);
+ }
+
+ return NULL;
}
static char *dev_generator(const char *text, int state)
{
- return generic_generator(text, state, dev_list, "Address");
+ return generic_generator(text, state,
+ default_ctrl ? default_ctrl->devices : NULL, "Address");
}
static char *attribute_generator(const char *text, int state)
@@ -1786,6 +1948,113 @@
return NULL;
}
+static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
+ const char **type)
+{
+ const char * const *opt;
+
+ if (arg == NULL || strlen(arg) == 0) {
+ rl_printf("Missing on/off/type argument\n");
+ return FALSE;
+ }
+
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ *value = TRUE;
+ *type = "";
+ return TRUE;
+ }
+
+ if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ *value = FALSE;
+ return TRUE;
+ }
+
+ for (opt = ad_arguments; *opt; opt++) {
+ if (strcmp(arg, *opt) == 0) {
+ *value = TRUE;
+ *type = *opt;
+ return TRUE;
+ }
+ }
+
+ rl_printf("Invalid argument %s\n", arg);
+ return FALSE;
+}
+
+static void cmd_advertise(const char *arg)
+{
+ dbus_bool_t enable;
+ const char *type;
+
+ if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+ return;
+
+ if (!ad_manager) {
+ rl_printf("LEAdvertisingManager not found\n");
+ return;
+ }
+
+ if (enable == TRUE)
+ ad_register(dbus_conn, ad_manager, type);
+ else
+ ad_unregister(dbus_conn, ad_manager);
+}
+
+static char *ad_generator(const char *text, int state)
+{
+ static int index, len;
+ const char *arg;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ while ((arg = ad_arguments[index])) {
+ index++;
+
+ if (!strncmp(arg, text, len))
+ return strdup(arg);
+ }
+
+ return NULL;
+}
+
+static void cmd_set_advertise_uuids(const char *arg)
+{
+ ad_advertise_uuids(arg);
+}
+
+static void cmd_set_advertise_service(const char *arg)
+{
+ ad_advertise_service(arg);
+}
+
+static void cmd_set_advertise_manufacturer(const char *arg)
+{
+ ad_advertise_manufacturer(arg);
+}
+
+static void cmd_set_advertise_tx_power(const char *arg)
+{
+ if (arg == NULL || strlen(arg) == 0) {
+ rl_printf("Missing on/off argument\n");
+ return;
+ }
+
+ if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+ ad_advertise_tx_power(TRUE);
+ return;
+ }
+
+ if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+ ad_advertise_tx_power(FALSE);
+ return;
+ }
+
+ rl_printf("Invalid argument\n");
+}
+
static const struct {
const char *cmd;
const char *arg;
@@ -1814,6 +2083,20 @@
capability_generator},
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
+ { "advertise", "<on/off/type>", cmd_advertise,
+ "Enable/disable advertising with given type",
+ ad_generator},
+ { "set-advertise-uuids", "[uuid1 uuid2 ...]",
+ cmd_set_advertise_uuids, "Set advertise uuids" },
+ { "set-advertise-service", "[uuid][data=[xx xx ...]",
+ cmd_set_advertise_service,
+ "Set advertise service data" },
+ { "set-advertise-manufacturer", "[id][data=[xx xx ...]",
+ cmd_set_advertise_manufacturer,
+ "Set advertise manufacturer data" },
+ { "set-advertise-tx-power", "<on/off>",
+ cmd_set_advertise_tx_power,
+ "Enable/disable TX power to be advertised" },
{ "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
cmd_set_scan_filter_uuids, "Set scan filter uuids" },
{ "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
@@ -1846,6 +2129,7 @@
dev_generator },
{ "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
dev_generator },
+ { "set-alias", "<alias>", cmd_set_alias, "Set device alias" },
{ "select-attribute", "<attribute>", cmd_select_attribute,
"Select attribute", attribute_generator },
{ "attribute-info", "[attribute]", cmd_attribute_info,
@@ -2158,7 +2442,6 @@
g_main_loop_unref(main_loop);
g_list_free_full(ctrl_list, proxy_leak);
- g_list_free_full(dev_list, proxy_leak);
g_free(auto_register_agent);
diff --git a/configure.ac b/configure.ac
index 2f459cc..530d512 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.41)
+AC_INIT(bluez, 5.43)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 97462a3..08de6cd 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -45,7 +45,7 @@
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.Failed
- void SetDiscoveryFilter(dict filter) [Experimental]
+ void SetDiscoveryFilter(dict filter)
This method sets the device discovery filter for the
caller. When this method is called with no filter
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 96832e8..13b2881 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -21,6 +21,20 @@
If at least one profile was connected successfully this
method will indicate success.
+ For dual-mode devices only one bearer is connected at
+ time, the conditions are in the following order:
+
+ 1. Connect the disconnected bearer if already
+ connected.
+
+ 2. Connect first the bonded bearer. If no
+ bearers are bonded or both are skip and check
+ latest seen bearer.
+
+ 3. Connect last seen bearer, in case the
+ timestamps are the same BR/EDR takes
+ precedence.
+
Possible errors: org.bluez.Error.NotReady
org.bluez.Error.Failed
org.bluez.Error.InProgress
@@ -196,7 +210,7 @@
Received Signal Strength Indicator of the remote
device (inquiry or advertising).
- int16 TxPower [readonly, optional, experimental]
+ int16 TxPower [readonly, optional]
Advertised transmitted power level (inquiry or
advertising).
@@ -212,7 +226,11 @@
Service advertisement data. Keys are the UUIDs in
string format followed by its byte array value.
- bool ServicesResolved [readonly, experimental]
+ bool ServicesResolved [readonly]
Indicate whether or not service discovery has been
resolved.
+
+ array{byte} AdvertisingFlags [readonly, experimental]
+
+ The Advertising Data Flags of the remote device.
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 4992243..6c98b87 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -27,7 +27,7 @@
properties defined in GattService1 interface.
Service org.bluez
-Interface org.bluez.GattService1 [Experimental]
+Interface org.bluez.GattService1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
Properties string UUID [read-only]
@@ -58,7 +58,7 @@
path hierarchy and are freely definable.
Service org.bluez
-Interface org.bluez.GattCharacteristic1 [Experimental]
+Interface org.bluez.GattCharacteristic1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
Methods array{byte} ReadValue(dict options)
@@ -115,7 +115,7 @@
object Service [read-only]
- Object path of the GATT service the characteristc
+ Object path of the GATT service the characteristic
belongs to.
array{byte} Value [read-only, optional]
@@ -159,7 +159,7 @@
Local or remote GATT characteristic descriptors hierarchy.
Service org.bluez
-Interface org.bluez.GattDescriptor1 [Experimental]
+Interface org.bluez.GattDescriptor1
Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ
Methods array{byte} ReadValue(dict flags)
@@ -198,7 +198,7 @@
object Characteristic [read-only]
- Object path of the GATT characteristc the descriptor
+ Object path of the GATT characteristic the descriptor
belongs to.
array{byte} Value [read-only, optional]
@@ -222,7 +222,7 @@
"secure-read" (Server Only)
"secure-write" (Server Only)
-GATT Profile hierarcy
+GATT Profile hierarchy
=====================
Local profile (GATT client) instance. By registering this type of object
@@ -231,7 +231,7 @@
supporting it.
Service <application dependent>
-Interface org.bluez.GattProfile1 [Experimental]
+Interface org.bluez.GattProfile1
Object path <application dependent>
Methods void Release()
@@ -307,8 +307,17 @@
all of its registered services will be automatically unregistered.
InterfacesAdded signals will be ignored.
+Examples:
+ - Client
+ test/example-gatt-client
+ client/bluetoothctl
+ - Server
+ test/example-gatt-server
+ tools/gatt-service
+
+
Service org.bluez
-Interface org.bluez.GattManager1 [Experimental]
+Interface org.bluez.GattManager1
Object path [variable prefix]/{hci0,hci1,...}
Methods void RegisterApplication(object application, dict options)
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 2bc2a88..34270dd 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -27,7 +27,8 @@
Linux kernel v4.2 Version 1.10
Linux kernel v4.5 Version 1.11
Linux kernel v4.6 Version 1.12
-Linux kernel v4.8 Version 1.13 (not yet released)
+Linux kernel v4.8 Version 1.13
+Linux kernel v4.9 Version 1.14 (not yet released)
Version 1.1 introduces Set Device ID command.
@@ -71,7 +72,8 @@
Version 1.10 does not introduce any new command or event. It extends the
advertising feature to support 5 parallel advertising instances.
-Version 1.11 introduces Get Advertising Size Information command.
+Version 1.11 introduces Get Advertising Size Information and Start Limited
+Discovery commands.
Version 1.12 introduces a new limited privacy mode (value 0x02 passed to
the Set Privacy command).
@@ -79,6 +81,11 @@
Version 1.13 introduces a new authentication failure reason code for the
Device Disconnected event.
+Version 1.14 introduces Read Extended Controller Information command and
+Extended Controller Information Changed event. It also adds Set Appearance
+command. The advertising flags Appearance and Local Name for adding scan
+response information are now supported.
+
Example
=======
@@ -2264,7 +2271,7 @@
Controller Index: <controller id>
Command Parameters: Address_Type (1 Octet)
RSSI_Threshold (1 Octet)
- UUID_Count (1 Octet)
+ UUID_Count (2 Octets)
UUID[i] (16 Octets)
Return Parameters: Address_Type (1 Octet)
@@ -2462,6 +2469,7 @@
0x06 SDIO
0x07 SPI
0x08 I2C
+ 0x09 SMD
Controllers marked as RAW only operation are currently not listed
by this command.
@@ -2810,6 +2818,105 @@
Invalid Index
+Read Extended Controller Information Command
+============================================
+
+ Command Code: 0x0042
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Address (6 Octets)
+ Bluetooth_Version (1 Octet)
+ Manufacturer (2 Octets)
+ Supported_Settings (4 Octets)
+ Current_Settings (4 Octets)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
+
+ This command is used to retrieve the current state and basic
+ information of a controller. It is typically used right after
+ getting the response to the Read Controller Index List command
+ or an Index Added event (or its extended counterparts).
+
+ The Address parameter describes the controllers public address
+ and it can be expected that it is set. However in case of single
+ mode Low Energy only controllers it can be 00:00:00:00:00:00. To
+ power on the controller in this case, it is required to configure
+ a static address using Set Static Address command first.
+
+ If the public address is set, then it will be used as identity
+ address for the controller. If no public address is available,
+ then the configured static address will be used as identity
+ address.
+
+ In the case of a dual-mode controller with public address that
+ is configured as Low Energy only device (BR/EDR switched off),
+ the static address is used when set and public address otherwise.
+
+ Current_Settings and Supported_Settings is a bitmask with
+ currently the following available bits:
+
+ 0 Powered
+ 1 Connectable
+ 2 Fast Connectable
+ 3 Discoverable
+ 4 Bondable
+ 5 Link Level Security (Sec. mode 3)
+ 6 Secure Simple Pairing
+ 7 Basic Rate/Enhanced Data Rate
+ 8 High Speed
+ 9 Low Energy
+ 10 Advertising
+ 11 Secure Connections
+ 12 Debug Keys
+ 13 Privacy
+ 14 Controller Configuration
+ 15 Static Address
+
+ The EIR_Data field contains information about class of device,
+ local name and other values. Not all of them might be present. For
+ example a Low Energy only device does not contain class of device
+ information.
+
+ When any of the values in the EIR_Data field changes, the event
+ Extended Controller Information Changed will be used to inform
+ clients about the updated information.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Invalid Parameters
+ Invalid Index
+
+
+Set Appearance Command
+======================
+
+ Command Code: 0x0042
+ Controller Index: <controller id>
+ Command Parameters: Appearance (2 Octets)
+ Return Parameters:
+
+ This command is used to set the appearance value of a controller.
+
+ This command can be used when the controller is not
+ powered and all settings will be programmed once powered.
+
+ The value of appearance will be remembered when switching
+ the controller off and back on again. So the appearance only
+ have to be set once when a new controller is found and will
+ stay until removed.
+
+ This command generates a Command Complete event on success
+ or a Command Status event on failure.
+
+ This command is only available for LE capable controllers.
+ It will return Not Supported otherwise.
+
+ Possible errors: Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
Command Complete Event
======================
@@ -3655,3 +3762,23 @@
The event will only be sent to management sockets other than the
one through which the command was sent.
+
+
+Extended Controller Information Changed Event
+=============================================
+
+ Event Code: 0x0025
+ Controller Index: <controller id>
+ Event Parameters: EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
+
+ This event indicates that controller information has been updated
+ and new values are used. This includes the local name, class of
+ device, device id and LE address information.
+
+ This event will only be used after Read Extended Controller
+ Information command has been used at least once. If it has not
+ been used the legacy events are used.
+
+ The event will only be sent to management sockets other than the
+ one through which the change was triggered.
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 2c34ec4..6a708b4 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -89,6 +89,18 @@
DiscoverableTimeout=0
+Identity file format
+====================
+Identity file contains one [General] group that holds identity information
+such as keys and adresses:
+
+ IdentityResolvingKey String 128-bit value of the IRK
+
+Sample:
+ [General]
+ IdentityResolvingKey=00112233445566778899aabbccddeeff
+
+
Attributes file format
======================
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 54e5fe4..741492a 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -39,7 +39,7 @@
Application Count Description
-------------------------------------------
-mgmt-tester 307 Kernel management interface testing
+mgmt-tester 331 Kernel management interface testing
l2cap-tester 33 Kernel L2CAP implementation testing
rfcomm-tester 9 Kernel RFCOMM implementation testing
bnep-tester 1 Kernel BNEP implementation testing
@@ -49,7 +49,7 @@
hci-tester 14 Controller hardware testing
userchan-tester 3 Kernel HCI User Channel testting
-----
- 384
+ 408
Android end-to-end testing
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index ea1e3cf..7debb8f 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -467,6 +467,16 @@
return true;
}
+bool hciemu_clear_master_post_command_hooks(struct hciemu *hciemu)
+{
+ if (!hciemu)
+ return false;
+
+ queue_remove_all(hciemu->post_command_hooks,
+ NULL, NULL, destroy_command_hook);
+ return true;
+}
+
int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
uint16_t opcode, hciemu_hook_func_t function,
void *user_data)
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index c5578d1..783f99c 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -66,6 +66,8 @@
bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
hciemu_command_func_t function, void *user_data);
+bool hciemu_clear_master_post_command_hooks(struct hciemu *hciemu);
+
int hciemu_add_hook(struct hciemu *hciemu, enum hciemu_hook_type type,
uint16_t opcode, hciemu_hook_func_t function,
void *user_data);
diff --git a/emulator/smp.c b/emulator/smp.c
index 40836cf..c30de36 100644
--- a/emulator/smp.c
+++ b/emulator/smp.c
@@ -370,7 +370,7 @@
conn->ra_type, conn->ra, confirm))
return false;
- if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf) != 0)) {
+ if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
printf("Confirmation values don't match\n");
return false;
}
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 3726b50..3276b68 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -540,7 +540,7 @@
case 138:
return "Jawbone";
case 139:
- return "Topcorn Positioning Systems, LLC";
+ return "Topcon Positioning Systems, LLC";
case 140:
return "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)";
case 141:
@@ -1078,7 +1078,7 @@
case 407:
return "WiSilica Inc";
case 408:
- return "Vengit Limited";
+ return "VENGIT Korlátolt Felelősségű Társaság";
case 409:
return "SALTO SYSTEMS S.L.";
case 410:
@@ -2143,6 +2143,200 @@
return "Meizu Technology Co., Ltd.";
case 940:
return "Smablo LTD";
+ case 941:
+ return "XiQ";
+ case 942:
+ return "Allswell Inc.";
+ case 943:
+ return "Comm-N-Sense Corp DBA Verigo";
+ case 944:
+ return "VIBRADORM GmbH";
+ case 945:
+ return "Otodata Wireless Network Inc.";
+ case 946:
+ return "Propagation Systems Limited";
+ case 947:
+ return "Midwest Instruments & Controls";
+ case 948:
+ return "Alpha Nodus, inc.";
+ case 949:
+ return "petPOMM, Inc";
+ case 950:
+ return "Mattel";
+ case 951:
+ return "Airbly Inc.";
+ case 952:
+ return "A-Safe Limited";
+ case 953:
+ return "FREDERIQUE CONSTANT SA";
+ case 954:
+ return "Maxscend Microelectronics Company Limited";
+ case 955:
+ return "Abbott Diabetes Care";
+ case 956:
+ return "ASB Bank Ltd";
+ case 957:
+ return "amadas";
+ case 958:
+ return "Applied Science, Inc.";
+ case 959:
+ return "iLumi Solutions Inc.";
+ case 960:
+ return "Arch Systems Inc.";
+ case 961:
+ return "Ember Technologies, Inc.";
+ case 962:
+ return "Snapchat Inc";
+ case 963:
+ return "Casambi Technologies Oy";
+ case 964:
+ return "Pico Technology Inc.";
+ case 965:
+ return "St. Jude Medical, Inc.";
+ case 966:
+ return "Intricon";
+ case 967:
+ return "Structural Health Systems, Inc.";
+ case 968:
+ return "Avvel International";
+ case 969:
+ return "Gallagher Group";
+ case 970:
+ return "In2things Automation Pvt. Ltd.";
+ case 971:
+ return "SYSDEV Srl";
+ case 972:
+ return "Vonkil Technologies Ltd";
+ case 973:
+ return "Wynd Technologies, Inc.";
+ case 974:
+ return "CONTRINEX S.A.";
+ case 975:
+ return "MIRA, Inc.";
+ case 976:
+ return "Watteam Ltd";
+ case 977:
+ return "Density Inc.";
+ case 978:
+ return "IOT Pot India Private Limited";
+ case 979:
+ return "Sigma Connectivity AB";
+ case 980:
+ return "PEG PEREGO SPA";
+ case 981:
+ return "Wyzelink Systems Inc.";
+ case 982:
+ return "Yota Devices LTD";
+ case 983:
+ return "FINSECUR";
+ case 984:
+ return "Zen-Me Labs Ltd";
+ case 985:
+ return "3IWare Co., Ltd.";
+ case 986:
+ return "EnOcean GmbH";
+ case 987:
+ return "Instabeat, Inc";
+ case 988:
+ return "Nima Labs";
+ case 989:
+ return "Andreas Stihl AG & Co. KG";
+ case 990:
+ return "Nathan Rhoades LLC";
+ case 991:
+ return "Grob Technologies, LLC";
+ case 992:
+ return "Actions (Zhuhai) Technology Co., Limited";
+ case 993:
+ return "SPD Development Company Ltd";
+ case 994:
+ return "Sensoan Oy";
+ case 995:
+ return "Qualcomm Life Inc";
+ case 996:
+ return "Chip-ing AG";
+ case 997:
+ return "ffly4u";
+ case 998:
+ return "IoT Instruments Oy";
+ case 999:
+ return "TRUE Fitness Technology";
+ case 1000:
+ return "Reiner Kartengeraete GmbH & Co. KG.";
+ case 1001:
+ return "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD.";
+ case 1002:
+ return "Hello Inc.";
+ case 1003:
+ return "Evollve Inc.";
+ case 1004:
+ return "Jigowatts Inc.";
+ case 1005:
+ return "BASIC MICRO.COM,INC.";
+ case 1006:
+ return "CUBE TECHNOLOGIES";
+ case 1007:
+ return "foolography GmbH";
+ case 1008:
+ return "CLINK";
+ case 1009:
+ return "Hestan Smart Cooking Inc.";
+ case 1010:
+ return "WindowMaster A/S";
+ case 1011:
+ return "Flowscape AB";
+ case 1012:
+ return "PAL Technologies Ltd";
+ case 1013:
+ return "WHERE, Inc.";
+ case 1014:
+ return "Iton Technology Corp.";
+ case 1015:
+ return "Owl Labs Inc.";
+ case 1016:
+ return "Rockford Corp.";
+ case 1017:
+ return "Becon Technologies Co.,Ltd.";
+ case 1018:
+ return "Vyassoft Technologies Inc";
+ case 1019:
+ return "Nox Medical";
+ case 1020:
+ return "Kimberly-Clark";
+ case 1021:
+ return "Trimble Navigation Ltd.";
+ case 1022:
+ return "Littelfuse";
+ case 1023:
+ return "Withings";
+ case 1024:
+ return "i-developer IT Beratung UG";
+ case 1025:
+ return "リレーションズ株式会社";
+ case 1026:
+ return "Sears Holdings Corporation";
+ case 1027:
+ return "Gantner Electronic GmbH";
+ case 1028:
+ return "Authomate Inc";
+ case 1029:
+ return "Vertex International, Inc.";
+ case 1030:
+ return "Airtago";
+ case 1031:
+ return "Swiss Audio SA";
+ case 1032:
+ return "ToGetHome Inc.";
+ case 1033:
+ return "AXIS";
+ case 1034:
+ return "Openmatics";
+ case 1035:
+ return "Jana Care Inc.";
+ case 1036:
+ return "Senix Corporation";
+ case 1037:
+ return "NorthStar Battery Company, LLC";
case 65535:
return "internal use";
default:
diff --git a/lib/hci.c b/lib/hci.c
index 0c85875..5ce39bd 100644
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -160,6 +160,8 @@
return "SPI";
case HCI_I2C:
return "I2C";
+ case HCI_SMD:
+ return "SMD";
default:
return "Unknown";
}
diff --git a/lib/hci.h b/lib/hci.h
index bdd0df0..3ef4311 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -57,6 +57,7 @@
#define HCI_SDIO 6
#define HCI_SPI 7
#define HCI_I2C 8
+#define HCI_SMD 9
/* HCI controller types */
#define HCI_PRIMARY 0x00
diff --git a/lib/mgmt.h b/lib/mgmt.h
index f6a976a..798a05e 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -530,6 +530,22 @@
#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041
+#define MGMT_OP_READ_EXT_INFO 0x0042
+struct mgmt_rp_read_ext_info {
+ bdaddr_t bdaddr;
+ uint8_t version;
+ uint16_t manufacturer;
+ uint32_t supported_settings;
+ uint32_t current_settings;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_OP_SET_APPEARANCE 0x0043
+struct mgmt_cp_set_appearance {
+ uint16_t appearance;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
@@ -742,6 +758,12 @@
uint8_t instance;
} __packed;
+#define MGMT_EV_EXT_INFO_CHANGED 0x0025
+struct mgmt_ev_ext_info_changed {
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
static const char *mgmt_op[] = {
"<0x0000>",
"Read Version",
@@ -809,6 +831,8 @@
"Remove Advertising",
"Get Advertising Size Information", /* 0x0040 */
"Start Limited Discovery",
+ "Read Extended Controller Information",
+ "Set Appearance",
};
static const char *mgmt_ev[] = {
@@ -849,6 +873,7 @@
"Local Out Of Band Extended Data Updated",
"Advertising Added",
"Advertising Removed",
+ "Extended Controller Information Changed",
};
static const char *mgmt_status[] = {
diff --git a/lib/uuid.c b/lib/uuid.c
index ac071fa..d4c7002 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -280,8 +280,11 @@
{
bt_uuid_t u1, u2;
- bt_string_to_uuid(&u1, a);
- bt_string_to_uuid(&u2, b);
+ if (bt_string_to_uuid(&u1, a) < 0)
+ return -EINVAL;
+
+ if (bt_string_to_uuid(&u2, b) < 0)
+ return -EINVAL;
return bt_uuid_cmp(&u1, &u2);
}
diff --git a/monitor/control.c b/monitor/control.c
index 26aecfd..9bbdc37 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -57,6 +57,7 @@
static struct btsnoop *btsnoop_file = NULL;
static bool hcidump_fallback = false;
+static bool decode_control = true;
struct control_data {
uint16_t channel;
@@ -797,6 +798,9 @@
void control_message(uint16_t opcode, const void *data, uint16_t size)
{
+ if (!decode_control)
+ return;
+
switch (opcode) {
case MGMT_EV_INDEX_ADDED:
mgmt_index_added(size, data);
@@ -1450,3 +1454,8 @@
return 0;
}
+
+void control_disable_decoding(void)
+{
+ decode_control = false;
+}
diff --git a/monitor/control.h b/monitor/control.h
index 55384db..630a852 100644
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -29,5 +29,6 @@
void control_server(const char *path);
int control_tty(const char *path, unsigned int speed);
int control_tracing(void);
+void control_disable_decoding(void);
void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/monitor/display.h b/monitor/display.h
index 36e189a..b85f37b 100644
--- a/monitor/display.h
+++ b/monitor/display.h
@@ -38,6 +38,11 @@
#define COLOR_WHITE_BG "\x1B[0;47;30m"
#define COLOR_HIGHLIGHT "\x1B[1;39m"
+#define COLOR_RED_BOLD "\x1B[1;31m"
+#define COLOR_GREEN_BOLD "\x1B[1;32m"
+#define COLOR_BLUE_BOLD "\x1B[1;34m"
+#define COLOR_MAGENTA_BOLD "\x1B[1;35m"
+
#define COLOR_ERROR "\x1B[1;31m"
#define COLOR_WARN "\x1B[1m"
#define COLOR_INFO COLOR_OFF
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index 582c853..ebc90e0 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -1999,7 +1999,7 @@
static void print_uuid(const char *label, const void *data, uint16_t size)
{
const char *str;
- char uuidstr[36];
+ char uuidstr[MAX_LEN_UUID_STR];
switch (size) {
case 2:
diff --git a/monitor/packet.c b/monitor/packet.c
index 0947213..45c9e8c 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -57,6 +57,7 @@
#include "broadcom.h"
#include "packet.h"
+#define COLOR_CHANNEL_LABEL COLOR_WHITE
#define COLOR_INDEX_LABEL COLOR_WHITE
#define COLOR_TIMESTAMP COLOR_YELLOW
@@ -66,13 +67,12 @@
#define COLOR_CLOSE_INDEX COLOR_RED
#define COLOR_INDEX_INFO COLOR_GREEN
#define COLOR_VENDOR_DIAG COLOR_YELLOW
+#define COLOR_SYSTEM_NOTE COLOR_OFF
#define COLOR_HCI_COMMAND COLOR_BLUE
#define COLOR_HCI_COMMAND_UNKNOWN COLOR_WHITE_BG
-
#define COLOR_HCI_EVENT COLOR_MAGENTA
#define COLOR_HCI_EVENT_UNKNOWN COLOR_WHITE_BG
-
#define COLOR_HCI_ACLDATA COLOR_CYAN
#define COLOR_HCI_SCODATA COLOR_YELLOW
@@ -84,6 +84,19 @@
#define COLOR_UNKNOWN_SERVICE_CLASS COLOR_WHITE_BG
#define COLOR_UNKNOWN_PKT_TYPE_BIT COLOR_WHITE_BG
+#define COLOR_CTRL_OPEN COLOR_GREEN_BOLD
+#define COLOR_CTRL_CLOSE COLOR_RED_BOLD
+#define COLOR_CTRL_COMMAND COLOR_BLUE_BOLD
+#define COLOR_CTRL_COMMAND_UNKNOWN COLOR_WHITE_BG
+#define COLOR_CTRL_EVENT COLOR_MAGENTA_BOLD
+#define COLOR_CTRL_EVENT_UNKNOWN COLOR_WHITE_BG
+
+#define COLOR_UNKNOWN_OPTIONS_BIT COLOR_WHITE_BG
+#define COLOR_UNKNOWN_SETTINGS_BIT COLOR_WHITE_BG
+#define COLOR_UNKNOWN_ADDRESS_TYPE COLOR_WHITE_BG
+#define COLOR_UNKNOWN_DEVICE_FLAG COLOR_WHITE_BG
+#define COLOR_UNKNOWN_ADV_FLAG COLOR_WHITE_BG
+
#define COLOR_PHY_PACKET COLOR_BLUE
static time_t time_offset = ((time_t) -1);
@@ -95,6 +108,71 @@
#define UNKNOWN_MANUFACTURER 0xffff
+#define CTRL_RAW 0x0000
+#define CTRL_USER 0x0001
+#define CTRL_MGMT 0x0002
+
+#define MAX_CTRL 64
+
+struct ctrl_data {
+ bool used;
+ uint32_t cookie;
+ uint16_t format;
+ char name[20];
+};
+
+static struct ctrl_data ctrl_list[MAX_CTRL];
+
+static void assign_ctrl(uint32_t cookie, uint16_t format, const char *name)
+{
+ int i;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (!ctrl_list[i].used) {
+ ctrl_list[i].used = true;
+ ctrl_list[i].cookie = cookie;
+ ctrl_list[i].format = format;
+ if (name) {
+ strncpy(ctrl_list[i].name, name, 19);
+ ctrl_list[i].name[19] = '\0';
+ } else
+ strcpy(ctrl_list[i].name, "null");
+ break;
+ }
+ }
+}
+
+static void release_ctrl(uint32_t cookie, uint16_t *format, char *name)
+{
+ int i;
+
+ if (format)
+ *format = 0xffff;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (ctrl_list[i].used && ctrl_list[i].cookie == cookie) {
+ ctrl_list[i].used = false;
+ if (format)
+ *format = ctrl_list[i].format;
+ if (name)
+ strncpy(name, ctrl_list[i].name, 20);
+ break;
+ }
+ }
+}
+
+static uint16_t get_format(uint32_t cookie)
+{
+ int i;
+
+ for (i = 0; i < MAX_CTRL; i++) {
+ if (ctrl_list[i].used && ctrl_list[i].cookie == cookie)
+ return ctrl_list[i].format;
+ }
+
+ return 0xffff;
+}
+
#define MAX_CONN 16
struct conn_data {
@@ -181,15 +259,29 @@
#define print_space(x) printf("%*c", (x), ' ');
-static void print_packet(struct timeval *tv, struct ucred *cred,
- uint16_t index, char ident,
+static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
+ uint16_t index, const char *channel,
const char *color, const char *label,
const char *text, const char *extra)
{
int col = num_columns();
- char line[256], ts_str[64];
+ char line[256], ts_str[96];
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
+ if (channel) {
+ if (use_color()) {
+ n = sprintf(ts_str + ts_pos, "%s", COLOR_CHANNEL_LABEL);
+ if (n > 0)
+ ts_pos += n;
+ }
+
+ n = sprintf(ts_str + ts_pos, " {%s}", channel);
+ if (n > 0) {
+ ts_pos += n;
+ ts_len += n;
+ }
+ }
+
if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
index != HCI_DEV_NONE) {
if (use_color()) {
@@ -2380,8 +2472,12 @@
break;
}
- print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)", label, str, version,
+ if (sublabel)
+ print_field("%s: %s (0x%2.2x) - %s %d (0x%4.4x)",
+ label, str, version,
sublabel, subversion, subversion);
+ else
+ print_field("%s: %s (0x%2.2x)", label, str, version);
}
static void print_hci_version(uint8_t version, uint16_t revision)
@@ -3297,16 +3393,18 @@
{
uint8_t count = data_len / 16;
unsigned int i;
+ char uuidstr[MAX_LEN_UUID_STR];
print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
for (i = 0; i < count; i++) {
const uint8_t *uuid = data + (i * 16);
- print_field(" %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+ sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
get_le32(&uuid[12]), get_le16(&uuid[10]),
get_le16(&uuid[8]), get_le16(&uuid[6]),
get_le32(&uuid[2]), get_le16(&uuid[0]));
+ print_field(" %s (%s)", uuidstr_to_str(uuidstr), uuidstr);
}
}
@@ -3802,9 +3900,22 @@
packet_user_logging(tv, cred, index, ul->priority, ident,
data + sizeof(*ul) + ul->ident_len);
break;
+ case BTSNOOP_OPCODE_CTRL_OPEN:
+ control_disable_decoding();
+ packet_ctrl_open(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_CLOSE:
+ packet_ctrl_close(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_COMMAND:
+ packet_ctrl_command(tv, cred, index, data, size);
+ break;
+ case BTSNOOP_OPCODE_CTRL_EVENT:
+ packet_ctrl_event(tv, cred, index, data, size);
+ break;
default:
sprintf(extra_str, "(code %d len %d)", opcode, size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Unknown packet", NULL, extra_str);
packet_hexdump(data, size);
break;
@@ -3821,7 +3932,7 @@
sprintf(str, "%u MHz", frequency);
- print_packet(tv, NULL, 0, '*', COLOR_PHY_PACKET,
+ print_packet(tv, NULL, '*', 0, NULL, COLOR_PHY_PACKET,
"Physical packet:", NULL, str);
ll_packet(frequency, data, size, false);
@@ -8663,26 +8774,26 @@
sprintf(details, "(%s,%s,%s)", hci_typetostr(type),
hci_bustostr(bus), name);
- print_packet(tv, NULL, index, '=', COLOR_NEW_INDEX, "New Index",
- label, details);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_NEW_INDEX,
+ "New Index", label, details);
}
void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_DEL_INDEX, "Delete Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_DEL_INDEX,
+ "Delete Index", label, NULL);
}
void packet_open_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_OPEN_INDEX, "Open Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_OPEN_INDEX,
+ "Open Index", label, NULL);
}
void packet_close_index(struct timeval *tv, uint16_t index, const char *label)
{
- print_packet(tv, NULL, index, '=', COLOR_CLOSE_INDEX, "Close Index",
- label, NULL);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_CLOSE_INDEX,
+ "Close Index", label, NULL);
}
void packet_index_info(struct timeval *tv, uint16_t index, const char *label,
@@ -8692,8 +8803,8 @@
sprintf(details, "(%s)", bt_compidtostr(manufacturer));
- print_packet(tv, NULL, index, '=', COLOR_INDEX_INFO, "Index Info",
- label, details);
+ print_packet(tv, NULL, '=', index, NULL, COLOR_INDEX_INFO,
+ "Index Info", label, details);
}
void packet_vendor_diag(struct timeval *tv, uint16_t index,
@@ -8704,7 +8815,7 @@
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, NULL, index, '=', COLOR_VENDOR_DIAG,
+ print_packet(tv, NULL, '=', index, NULL, COLOR_VENDOR_DIAG,
"Vendor Diagnostic", NULL, extra_str);
switch (manufacturer) {
@@ -8720,7 +8831,8 @@
void packet_system_note(struct timeval *tv, struct ucred *cred,
uint16_t index, const void *message)
{
- print_packet(tv, cred, index, '=', COLOR_INFO, "Note", message, NULL);
+ print_packet(tv, cred, '=', index, NULL, COLOR_SYSTEM_NOTE,
+ "Note", message, NULL);
}
void packet_user_logging(struct timeval *tv, struct ucred *cred,
@@ -8780,7 +8892,7 @@
label = "Message";
}
- print_packet(tv, cred, index, '=', color, label, message, NULL);
+ print_packet(tv, cred, '=', index, NULL, color, label, message, NULL);
}
void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
@@ -8798,7 +8910,7 @@
if (size < HCI_COMMAND_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed HCI Command packet", NULL, extra_str);
packet_hexdump(data, size);
return;
@@ -8856,7 +8968,7 @@
sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen);
- print_packet(tv, cred, index, '<', opcode_color, "HCI Command",
+ print_packet(tv, cred, '<', index, NULL, opcode_color, "HCI Command",
opcode_str, extra_str);
if (!opcode_data || !opcode_data->cmd_func) {
@@ -8899,7 +9011,7 @@
if (size < HCI_EVENT_HDR_SIZE) {
sprintf(extra_str, "(len %d)", size);
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed HCI Event packet", NULL, extra_str);
packet_hexdump(data, size);
return;
@@ -8936,7 +9048,7 @@
sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen);
- print_packet(tv, cred, index, '>', event_color, "HCI Event",
+ print_packet(tv, cred, '>', index, NULL, event_color, "HCI Event",
event_str, extra_str);
if (!event_data || !event_data->func) {
@@ -8977,24 +9089,24 @@
uint8_t flags = acl_flags(handle);
char handle_str[16], extra_str[32];
- if (size < sizeof(*hdr)) {
+ if (size < HCI_ACL_HDR_SIZE) {
if (in)
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed ACL Data RX packet", NULL, NULL);
else
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed ACL Data TX packet", NULL, NULL);
packet_hexdump(data, size);
return;
}
- data += sizeof(*hdr);
- size -= sizeof(*hdr);
+ data += HCI_ACL_HDR_SIZE;
+ size -= HCI_ACL_HDR_SIZE;
sprintf(handle_str, "Handle %d", acl_handle(handle));
sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, dlen);
- print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_ACLDATA,
+ print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_ACLDATA,
in ? "ACL Data RX" : "ACL Data TX",
handle_str, extra_str);
@@ -9021,10 +9133,10 @@
if (size < HCI_SCO_HDR_SIZE) {
if (in)
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed SCO Data RX packet", NULL, NULL);
else
- print_packet(tv, cred, index, '*', COLOR_ERROR,
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
"Malformed SCO Data TX packet", NULL, NULL);
packet_hexdump(data, size);
return;
@@ -9036,7 +9148,7 @@
sprintf(handle_str, "Handle %d", acl_handle(handle));
sprintf(extra_str, "flags 0x%2.2x dlen %d", flags, hdr->dlen);
- print_packet(tv, cred, index, in ? '>' : '<', COLOR_HCI_SCODATA,
+ print_packet(tv, cred, in ? '>' : '<', index, NULL, COLOR_HCI_SCODATA,
in ? "SCO Data RX" : "SCO Data TX",
handle_str, extra_str);
@@ -9051,6 +9163,2514 @@
packet_hexdump(data, size);
}
+void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format;
+ char channel[11];
+
+ if (size < 6) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Open packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+ format = get_le16(data + 4);
+
+ data += 6;
+ size -= 6;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ if ((format == CTRL_RAW || format == CTRL_USER || format == CTRL_MGMT)
+ && size >= 8) {
+ uint8_t version;
+ uint16_t revision;
+ uint32_t flags;
+ uint8_t ident_len;
+ const char *comm;
+ char details[48];
+ const char *title;
+
+ version = get_u8(data);
+ revision = get_le16(data + 1);
+ flags = get_le32(data + 3);
+ ident_len = get_u8(data + 7);
+
+ data += 8;
+ size -= 8;
+
+ comm = ident_len > 0 ? data : "unknown";
+
+ data += ident_len;
+ size -= ident_len;
+
+ assign_ctrl(cookie, format, comm);
+
+ sprintf(details, "%sversion %u.%u",
+ flags & 0x0001 ? "(privileged) " : "",
+ version, revision);
+
+ switch (format) {
+ case CTRL_RAW:
+ title = "RAW Open";
+ break;
+ case CTRL_USER:
+ title = "USER Open";
+ break;
+ case CTRL_MGMT:
+ title = "MGMT Open";
+ break;
+ default:
+ title = "Control Open";
+ break;
+ }
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+ title, comm, details);
+ } else {
+ char label[7];
+
+ assign_ctrl(cookie, format, NULL);
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_OPEN,
+ "Control Open", label, NULL);
+ }
+
+ packet_hexdump(data, size);
+}
+
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format;
+ char channel[11], label[22];
+ const char *title;
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Close packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ release_ctrl(cookie, &format, label);
+
+ switch (format) {
+ case CTRL_RAW:
+ title = "RAW Close";
+ break;
+ case CTRL_USER:
+ title = "USER Close";
+ break;
+ case CTRL_MGMT:
+ title = "MGMT Close";
+ break;
+ default:
+ sprintf(label, "0x%4.4x", format);
+ title = "Control Close";
+ break;
+ }
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ title, label, NULL);
+
+ packet_hexdump(data, size);
+}
+
+static const struct {
+ uint8_t status;
+ const char *str;
+} mgmt_status_table[] = {
+ { 0x00, "Success" },
+ { 0x01, "Unknown Command" },
+ { 0x02, "Not Connected" },
+ { 0x03, "Failed" },
+ { 0x04, "Connect Failed" },
+ { 0x05, "Authentication Failed" },
+ { 0x06, "Not Paired" },
+ { 0x07, "No Resources" },
+ { 0x08, "Timeout" },
+ { 0x09, "Already Connected" },
+ { 0x0a, "Busy" },
+ { 0x0b, "Rejected" },
+ { 0x0c, "Not Supported" },
+ { 0x0d, "Invalid Parameters" },
+ { 0x0e, "Disconnected" },
+ { 0x0f, "Not Powered" },
+ { 0x10, "Cancelled" },
+ { 0x11, "Invalid Index" },
+ { 0x12, "RFKilled" },
+ { 0x13, "Already Paired" },
+ { 0x14, "Permission Denied" },
+ { }
+};
+
+static void mgmt_print_status(uint8_t status)
+{
+ const char *str = "Unknown";
+ const char *color_on, *color_off;
+ bool unknown = true;
+ int i;
+
+ for (i = 0; mgmt_status_table[i].str; i++) {
+ if (mgmt_status_table[i].status == status) {
+ str = mgmt_status_table[i].str;
+ unknown = false;
+ break;
+ }
+ }
+
+ if (use_color()) {
+ if (status) {
+ if (unknown)
+ color_on = COLOR_UNKNOWN_ERROR;
+ else
+ color_on = COLOR_RED;
+ } else
+ color_on = COLOR_GREEN;
+ color_off = COLOR_OFF;
+ } else {
+ color_on = "";
+ color_off = "";
+ }
+
+ print_field("Status: %s%s%s (0x%2.2x)",
+ color_on, str, color_off, status);
+}
+
+static void mgmt_print_address(const uint8_t *address, uint8_t type)
+{
+ switch (type) {
+ case 0x00:
+ print_addr_resolve("BR/EDR Address", address, 0x00, false);
+ break;
+ case 0x01:
+ print_addr_resolve("LE Address", address, 0x00, false);
+ break;
+ case 0x02:
+ print_addr_resolve("LE Address", address, 0x01, false);
+ break;
+ default:
+ print_addr_resolve("Address", address, 0xff, false);
+ break;
+ }
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_address_type_table[] = {
+ { 0, "BR/EDR" },
+ { 1, "LE Public" },
+ { 2, "LE Random" },
+ { }
+};
+
+static void mgmt_print_address_type(uint8_t type)
+{
+ uint8_t mask = type;
+ int i;
+
+ print_field("Address type: 0x%2.2x", type);
+
+ for (i = 0; mgmt_address_type_table[i].str; i++) {
+ if (type & (1 << mgmt_address_type_table[i].bit)) {
+ print_field(" %s", mgmt_address_type_table[i].str);
+ mask &= ~(1 << mgmt_address_type_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADDRESS_TYPE, " Unknown address type"
+ " (0x%2.2x)", mask);
+}
+
+static void mgmt_print_version(uint8_t version)
+{
+ packet_print_version("Version", version, NULL, 0x0000);
+}
+
+static void mgmt_print_manufacturer(uint16_t manufacturer)
+{
+ packet_print_company("Manufacturer", manufacturer);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_options_table[] = {
+ { 0, "External configuration" },
+ { 1, "Bluetooth public address configuration" },
+ { }
+};
+
+static void mgmt_print_options(const char *label, uint32_t options)
+{
+ uint32_t mask = options;
+ int i;
+
+ print_field("%s: 0x%8.8x", label, options);
+
+ for (i = 0; mgmt_options_table[i].str; i++) {
+ if (options & (1 << mgmt_options_table[i].bit)) {
+ print_field(" %s", mgmt_options_table[i].str);
+ mask &= ~(1 << mgmt_options_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_OPTIONS_BIT, " Unknown options"
+ " (0x%8.8x)", mask);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_settings_table[] = {
+ { 0, "Powered" },
+ { 1, "Connectable" },
+ { 2, "Fast Connectable" },
+ { 3, "Discoverable" },
+ { 4, "Bondable" },
+ { 5, "Link Security" },
+ { 6, "Secure Simple Pairing" },
+ { 7, "BR/EDR" },
+ { 8, "High Speed" },
+ { 9, "Low Energy" },
+ { 10, "Advertising" },
+ { 11, "Secure Connections" },
+ { 12, "Debug Keys" },
+ { 13, "Privacy" },
+ { 14, "Controller Configuration"},
+ { 15, "Static Address" },
+ { }
+};
+
+static void mgmt_print_settings(const char *label, uint32_t settings)
+{
+ uint32_t mask = settings;
+ int i;
+
+ print_field("%s: 0x%8.8x", label, settings);
+
+ for (i = 0; mgmt_settings_table[i].str; i++) {
+ if (settings & (1 << mgmt_settings_table[i].bit)) {
+ print_field(" %s", mgmt_settings_table[i].str);
+ mask &= ~(1 << mgmt_settings_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_SETTINGS_BIT, " Unknown settings"
+ " (0x%8.8x)", mask);
+}
+
+static void mgmt_print_name(const void *data)
+{
+ print_field("Name: %s", (char *) data);
+ print_field("Short name: %s", (char *) (data + 249));
+}
+
+static void mgmt_print_uuid(const void *data)
+{
+ const uint8_t *uuid = data;
+
+ print_field("UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+ get_le32(&uuid[12]), get_le16(&uuid[10]),
+ get_le16(&uuid[8]), get_le16(&uuid[6]),
+ get_le32(&uuid[2]), get_le16(&uuid[0]));
+}
+
+static void mgmt_print_enable(const char *label, uint8_t enable)
+{
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s: %s (0x%2.2x)", label, str, enable);
+}
+
+static void mgmt_print_io_capability(uint8_t capability)
+{
+ const char *str;
+
+ switch (capability) {
+ case 0x00:
+ str = "DisplayOnly";
+ break;
+ case 0x01:
+ str = "DisplayYesNo";
+ break;
+ case 0x02:
+ str = "KeyboardOnly";
+ break;
+ case 0x03:
+ str = "NoInputNoOutput";
+ break;
+ case 0x04:
+ str = "KeyboardDisplay";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Capability: %s (0x%2.2x)", str, capability);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_device_flags_table[] = {
+ { 0, "Confirm Name" },
+ { 1, "Legacy Pairing" },
+ { 2, "Not Connectable" },
+ { }
+};
+
+static void mgmt_print_device_flags(uint32_t flags)
+{
+ uint32_t mask = flags;
+ int i;
+
+ print_field("Flags: 0x%8.8x", flags);
+
+ for (i = 0; mgmt_device_flags_table[i].str; i++) {
+ if (flags & (1 << mgmt_device_flags_table[i].bit)) {
+ print_field(" %s", mgmt_device_flags_table[i].str);
+ mask &= ~(1 << mgmt_device_flags_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_DEVICE_FLAG, " Unknown device flag"
+ " (0x%8.8x)", mask);
+}
+
+static void mgmt_print_device_action(uint8_t action)
+{
+ const char *str;
+
+ switch (action) {
+ case 0x00:
+ str = "Background scan for device";
+ break;
+ case 0x01:
+ str = "Allow incoming connection";
+ break;
+ case 0x02:
+ str = "Auto-connect remote device";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action: %s (0x%2.2x)", str, action);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} mgmt_adv_flags_table[] = {
+ { 0, "Switch into Connectable mode" },
+ { 1, "Advertise as Discoverable" },
+ { 2, "Advertise as Limited Discoverable" },
+ { 3, "Add Flags field to Advertising Data" },
+ { 4, "Add TX Power field to Advertising Data" },
+ { 5, "Add Appearance field to Scan Response" },
+ { 6, "Add Local Name in Scan Response" },
+ { }
+};
+
+static void mgmt_print_adv_flags(uint32_t flags)
+{
+ uint32_t mask = flags;
+ int i;
+
+ print_field("Flags: 0x%8.8x", flags);
+
+ for (i = 0; mgmt_adv_flags_table[i].str; i++) {
+ if (flags & (1 << mgmt_adv_flags_table[i].bit)) {
+ print_field(" %s", mgmt_adv_flags_table[i].str);
+ mask &= ~(1 << mgmt_adv_flags_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_text(COLOR_UNKNOWN_ADV_FLAG, " Unknown advertising flag"
+ " (0x%8.8x)", mask);
+}
+
+static void mgmt_print_store_hint(uint8_t hint)
+{
+ const char *str;
+
+ switch (hint) {
+ case 0x00:
+ str = "No";
+ break;
+ case 0x01:
+ str = "Yes";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Store hint: %s (0x%2.2x)", str, hint);
+}
+
+static void mgmt_print_connection_parameter(const void *data)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint16_t min_conn_interval = get_le16(data + 7);
+ uint16_t max_conn_interval = get_le16(data + 9);
+ uint16_t conn_latency = get_le16(data + 11);
+ uint16_t supv_timeout = get_le16(data + 13);
+
+ mgmt_print_address(data, address_type);
+ print_field("Min connection interval: %u", min_conn_interval);
+ print_field("Max connection interval: %u", max_conn_interval);
+ print_field("Connection latency: %u", conn_latency);
+ print_field("Supervision timeout: %u", supv_timeout);
+}
+
+static void mgmt_print_link_key(const void *data)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t key_type = get_u8(data + 7);
+ uint8_t pin_len = get_u8(data + 24);
+
+ mgmt_print_address(data, address_type);
+ print_key_type(key_type);
+ print_link_key(data + 8);
+ print_field("PIN length: %d", pin_len);
+}
+
+static void mgmt_print_long_term_key(const void *data)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t key_type = get_u8(data + 7);
+ uint8_t master = get_u8(data + 8);
+ uint8_t enc_size = get_u8(data + 9);
+ const char *str;
+
+ mgmt_print_address(data, address_type);
+
+ switch (key_type) {
+ case 0x00:
+ str = "Unauthenticated legacy key";
+ break;
+ case 0x01:
+ str = "Authenticated legacy key";
+ break;
+ case 0x02:
+ str = "Unauthenticated key from P-256";
+ break;
+ case 0x03:
+ str = "Authenticated key from P-256";
+ break;
+ case 0x04:
+ str = "Debug key from P-256";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Key type: %s (0x%2.2x)", str, key_type);
+ print_field("Master: 0x%2.2x", master);
+ print_field("Encryption size: %u", enc_size);
+ print_hex_field("Diversifier", data + 10, 2);
+ print_hex_field("Randomizer", data + 12, 8);
+ print_hex_field("Key", data + 20, 16);
+}
+
+static void mgmt_print_identity_resolving_key(const void *data)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+ print_hex_field("Key", data + 7, 16);
+}
+
+static void mgmt_print_signature_resolving_key(const void *data)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t key_type = get_u8(data + 7);
+ const char *str;
+
+ mgmt_print_address(data, address_type);
+
+ switch (key_type) {
+ case 0x00:
+ str = "Unauthenticated local CSRK";
+ break;
+ case 0x01:
+ str = "Unauthenticated remote CSRK";
+ break;
+ case 0x02:
+ str = "Authenticated local CSRK";
+ break;
+ case 0x03:
+ str = "Authenticated remote CSRK";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Key type: %s (0x%2.2x)", str, key_type);
+ print_hex_field("Key", data + 8, 16);
+}
+
+static void mgmt_print_oob_data(const void *data)
+{
+ print_hash_p192(data);
+ print_randomizer_p192(data + 16);
+ print_hash_p256(data + 32);
+ print_randomizer_p256(data + 48);
+}
+
+static void mgmt_null_cmd(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_null_rsp(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_read_version_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t version;
+ uint16_t revision;
+
+ version = get_u8(data);
+ revision = get_le16(data + 1);
+
+ print_field("Version: %u.%u", version, revision);
+}
+
+static void mgmt_print_commands(const void *data, uint16_t num);
+static void mgmt_print_events(const void *data, uint16_t num);
+
+static void mgmt_read_supported_commands_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_commands = get_le16(data);
+ uint16_t num_events = get_le16(data + 2);
+
+ if (size - 4 != (num_commands * 2) + (num_events *2)) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ mgmt_print_commands(data + 4, num_commands);
+ mgmt_print_events(data + 4 + num_commands * 2, num_events);
+}
+
+static void mgmt_read_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers = get_le16(data);
+ int i;
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 2) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 2));
+
+ print_field(" hci%u", index);
+ }
+}
+
+static void mgmt_read_controller_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t version = get_u8(data + 6);
+ uint16_t manufacturer = get_le16(data + 7);
+ uint32_t supported_settings = get_le32(data + 9);
+ uint32_t current_settings = get_le32(data + 13);
+
+ print_addr_resolve("Address", data, 0x00, false);
+ mgmt_print_version(version);
+ mgmt_print_manufacturer(manufacturer);
+ mgmt_print_settings("Supported settings", supported_settings);
+ mgmt_print_settings("Current settings", current_settings);
+ print_dev_class(data + 17);
+ mgmt_print_name(data + 20);
+}
+
+static void mgmt_set_powered_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Powered", enable);
+}
+
+static void mgmt_set_discoverable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ uint16_t timeout = get_le16(data + 1);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "General";
+ break;
+ case 0x02:
+ str = "Limited";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Discoverable: %s (0x%2.2x)", str, enable);
+ print_field("Timeout: %u", timeout);
+}
+
+static void mgmt_set_connectable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Connectable", enable);
+}
+
+static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Fast Connectable", enable);
+}
+
+static void mgmt_set_bondable_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Bondable", enable);
+}
+
+static void mgmt_set_link_security_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Link Security", enable);
+}
+
+static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Secure Simple Pairing", enable);
+}
+
+static void mgmt_set_high_speed_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("High Speed", enable);
+}
+
+static void mgmt_set_low_energy_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Low Energy", enable);
+}
+
+static void mgmt_new_settings_rsp(const void *data, uint16_t size)
+{
+ uint32_t current_settings = get_le32(data);
+
+ mgmt_print_settings("Current settings", current_settings);
+}
+
+static void mgmt_set_device_class_cmd(const void *data, uint16_t size)
+{
+ uint8_t major = get_u8(data);
+ uint8_t minor = get_u8(data + 1);
+
+ print_field("Major class: 0x%2.2x", major);
+ print_field("Minor class: 0x%2.2x", minor);
+}
+
+static void mgmt_set_device_class_rsp(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_set_local_name_cmd(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static void mgmt_set_local_name_rsp(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static void mgmt_add_uuid_cmd(const void *data, uint16_t size)
+{
+ uint8_t service_class = get_u8(data + 16);
+
+ mgmt_print_uuid(data);
+ print_field("Service class: 0x%2.2x", service_class);
+}
+
+static void mgmt_add_uuid_rsp(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_remove_uuid_cmd(const void *data, uint16_t size)
+{
+ mgmt_print_uuid(data);
+}
+
+static void mgmt_remove_uuid_rsp(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_load_link_keys_cmd(const void *data, uint16_t size)
+{
+ uint8_t debug_keys = get_u8(data);
+ uint16_t num_keys = get_le16(data + 1);
+ int i;
+
+ mgmt_print_enable("Debug keys", debug_keys);
+ print_field("Keys: %u", num_keys);
+
+ if (size - 3 != num_keys * 25) {
+ packet_hexdump(data + 3, size - 3);
+ return;
+ }
+
+ for (i = 0; i < num_keys; i++)
+ mgmt_print_link_key(data + 3 + (i * 25));
+}
+
+static void mgmt_load_long_term_keys_cmd(const void *data, uint16_t size)
+{
+ uint16_t num_keys = get_le16(data + 1);
+ int i;
+
+ print_field("Keys: %u", num_keys);
+
+ if (size - 2 != num_keys * 36) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_keys; i++)
+ mgmt_print_long_term_key(data + 2 + (i * 36));
+}
+
+static void mgmt_disconnect_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_disconnect_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_connections_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_connections = get_le16(data);
+ int i;
+
+ print_field("Connections: %u", num_connections);
+
+ if (size - 2 != num_connections * 7) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_connections; i++) {
+ uint8_t address_type = get_u8(data + 2 + (i * 7) + 6);
+
+ mgmt_print_address(data + 2 + (i * 7), address_type);
+ }
+}
+
+static void mgmt_pin_code_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t pin_len = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ print_field("PIN length: %u", pin_len);
+ print_hex_field("PIN code", data + 8, 16);
+}
+
+static void mgmt_pin_code_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_pin_code_neg_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_pin_code_neg_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_set_io_capability_cmd(const void *data, uint16_t size)
+{
+ uint8_t capability = get_u8(data);
+
+ mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t capability = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_io_capability(capability);
+}
+
+static void mgmt_pair_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_cancel_pair_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unpair_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t disconnect = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_enable("Disconnect", disconnect);
+}
+
+static void mgmt_unpair_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_neg_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_confirmation_neg_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint32_t passkey = get_le32(data + 7);
+
+ mgmt_print_address(data, address_type);
+ print_field("Passkey: 0x%4.4x", passkey);
+}
+
+static void mgmt_user_passkey_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_neg_reply_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_user_passkey_neg_reply_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_read_local_oob_data_rsp(const void *data, uint16_t size)
+{
+ mgmt_print_oob_data(data);
+}
+
+static void mgmt_add_remote_oob_data_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_oob_data(data + 7);
+}
+
+static void mgmt_add_remote_oob_data_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_remote_oob_data_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_remote_oob_data_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_start_discovery_cmd(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_start_discovery_rsp(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_stop_discovery_cmd(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_stop_discovery_rsp(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_confirm_name_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t name_known = get_u8(data + 7);
+ const char *str;
+
+ mgmt_print_address(data, address_type);
+
+ switch (name_known) {
+ case 0x00:
+ str = "No";
+ break;
+ case 0x01:
+ str = "Yes";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Name known: %s (0x%2.2x)", str, name_known);
+}
+
+static void mgmt_confirm_name_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_block_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_block_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unblock_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_unblock_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_set_device_id_cmd(const void *data, uint16_t size)
+{
+ print_device_id(data, size);
+}
+
+static void mgmt_set_advertising_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Connectable";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Advertising: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_bredr_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("BR/EDR", enable);
+}
+
+static void mgmt_set_static_address_cmd(const void *data, uint16_t size)
+{
+ print_addr_resolve("Address", data, 0x01, false);
+}
+
+static void mgmt_set_scan_parameters_cmd(const void *data, uint16_t size)
+{
+ uint16_t interval = get_le16(data);
+ uint16_t window = get_le16(data + 2);
+
+ print_field("Interval: %u (0x%2.2x)", interval, interval);
+ print_field("Window: %u (0x%2.2x)", window, window);
+}
+
+static void mgmt_set_secure_connections_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Only";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Secure Connections: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_debug_keys_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Generate";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Debug Keys: %s (0x%2.2x)", str, enable);
+}
+
+static void mgmt_set_privacy_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ case 0x02:
+ str = "Limited";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Privacy: %s (0x%2.2x)", str, enable);
+ print_hex_field("Key", data + 1, 16);
+}
+
+static void mgmt_load_identity_resolving_keys_cmd(const void *data, uint16_t size)
+{
+ uint16_t num_keys = get_le16(data + 1);
+ int i;
+
+ print_field("Keys: %u", num_keys);
+
+ if (size - 2 != num_keys * 23) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_keys; i++)
+ mgmt_print_identity_resolving_key(data + 2 + (i * 23));
+}
+
+static void mgmt_get_connection_information_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_connection_information_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ int8_t rssi = get_s8(data + 7);
+ int8_t tx_power = get_s8(data + 8);
+ int8_t max_tx_power = get_s8(data + 9);
+
+ mgmt_print_address(data, address_type);
+ print_rssi(rssi);
+ print_power_level(tx_power, NULL);
+ print_power_level(max_tx_power, "max");
+}
+
+static void mgmt_get_clock_information_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_get_clock_information_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint32_t local_clock = get_le32(data + 7);
+ uint32_t piconet_clock = get_le32(data + 11);
+ uint16_t accuracy = get_le16(data + 15);
+
+ mgmt_print_address(data, address_type);
+ print_field("Local clock: 0x%8.8x", local_clock);
+ print_field("Piconet clock: 0x%8.8x", piconet_clock);
+ print_field("Accuracy: 0x%4.4x", accuracy);
+}
+
+static void mgmt_add_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t action = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_device_action(action);
+}
+
+static void mgmt_add_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_device_cmd(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_remove_device_rsp(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_load_connection_parameters_cmd(const void *data, uint16_t size)
+{
+ uint16_t num_parameters = get_le16(data);
+ int i;
+
+ print_field("Parameters: %u", num_parameters);
+
+ if (size - 2 != num_parameters * 15) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_parameters; i++)
+ mgmt_print_connection_parameter(data + 2 + (i * 15));
+}
+
+static void mgmt_read_unconf_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers = get_le16(data);
+ int i;
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 2) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 2));
+
+ print_field(" hci%u", index);
+ }
+}
+
+static void mgmt_read_controller_conf_info_rsp(const void *data, uint16_t size)
+{
+ uint16_t manufacturer = get_le16(data);
+ uint32_t supported_options = get_le32(data + 2);
+ uint32_t missing_options = get_le32(data + 6);
+
+ mgmt_print_manufacturer(manufacturer);
+ mgmt_print_options("Supported options", supported_options);
+ mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_set_external_configuration_cmd(const void *data, uint16_t size)
+{
+ uint8_t enable = get_u8(data);
+
+ mgmt_print_enable("Configuration", enable);
+}
+
+static void mgmt_set_public_address_cmd(const void *data, uint16_t size)
+{
+ print_addr_resolve("Address", data, 0x00, false);
+}
+
+static void mgmt_new_options_rsp(const void *data, uint16_t size)
+{
+ uint32_t missing_options = get_le32(data);
+
+ mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_start_service_discovery_cmd(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ int8_t rssi = get_s8(data + 1);
+ uint16_t num_uuids = get_le16(data + 2);
+ int i;
+
+ mgmt_print_address_type(type);
+ print_rssi(rssi);
+ print_field("UUIDs: %u", num_uuids);
+
+ if (size - 4 != num_uuids * 16) {
+ packet_hexdump(data + 4, size - 4);
+ return;
+ }
+
+ for (i = 0; i < num_uuids; i++)
+ mgmt_print_uuid(data + 4 + (i * 16));
+}
+
+static void mgmt_start_service_discovery_rsp(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_read_ext_index_list_rsp(const void *data, uint16_t size)
+{
+ uint16_t num_controllers = get_le16(data);
+ int i;
+
+ print_field("Controllers: %u", num_controllers);
+
+ if (size - 2 != num_controllers * 4) {
+ packet_hexdump(data + 2, size - 2);
+ return;
+ }
+
+ for (i = 0; i < num_controllers; i++) {
+ uint16_t index = get_le16(data + 2 + (i * 4));
+ uint8_t type = get_u8(data + 4 + (i * 4));
+ uint8_t bus = get_u8(data + 5 + (i * 4));
+ const char *str;
+
+ switch (type) {
+ case 0x00:
+ str = "Primary";
+ break;
+ case 0x01:
+ str = "Unconfigured";
+ break;
+ case 0x02:
+ str = "AMP";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field(" hci%u (%s,%s)", index, str, hci_bustostr(bus));
+ }
+}
+
+static void mgmt_read_local_oob_ext_data_cmd(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_read_local_oob_ext_data_rsp(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ uint16_t data_len = get_le16(data + 1);
+
+ mgmt_print_address_type(type);
+ print_field("Data length: %u", data_len);
+ print_eir(data + 3, size - 3, true);
+}
+
+static void mgmt_read_advertising_features_rsp(const void *data, uint16_t size)
+{
+ uint32_t flags = get_le32(data);
+ uint8_t adv_data_len = get_u8(data + 4);
+ uint8_t scan_rsp_len = get_u8(data + 5);
+ uint8_t max_instances = get_u8(data + 6);
+ uint8_t num_instances = get_u8(data + 7);
+ int i;
+
+ mgmt_print_adv_flags(flags);
+ print_field("Advertising data length: %u", adv_data_len);
+ print_field("Scan response length: %u", scan_rsp_len);
+ print_field("Max instances: %u", max_instances);
+ print_field("Instances: %u", num_instances);
+
+ if (size - 8 != num_instances) {
+ packet_hexdump(data + 8, size - 8);
+ return;
+ }
+
+ for (i = 0; i < num_instances; i++) {
+ uint8_t instance = get_u8(data + 8 + i);
+
+ print_field(" %u", instance);
+ }
+}
+
+static void mgmt_add_advertising_cmd(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+ uint32_t flags = get_le32(data + 1);
+ uint16_t duration = get_le16(data + 5);
+ uint16_t timeout = get_le16(data + 7);
+ uint8_t adv_data_len = get_u8(data + 9);
+ uint8_t scan_rsp_len = get_u8(data + 10);
+
+ print_field("Instance: %u", instance);
+ mgmt_print_adv_flags(flags);
+ print_field("Duration: %u", duration);
+ print_field("Timeout: %u", timeout);
+ print_field("Advertising data length: %u", adv_data_len);
+ print_eir(data + 11, adv_data_len, false);
+ print_field("Scan response length: %u", scan_rsp_len);
+ print_eir(data + 11 + adv_data_len, scan_rsp_len, false);
+}
+
+static void mgmt_add_advertising_rsp(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+
+ print_field("Instance: %u", instance);
+}
+
+static void mgmt_remove_advertising_cmd(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+
+ print_field("Instance: %u", instance);
+}
+
+static void mgmt_remove_advertising_rsp(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+
+ print_field("Instance: %u", instance);
+}
+
+static void mgmt_get_advertising_size_info_cmd(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+ uint32_t flags = get_le32(data + 1);
+
+ print_field("Instance: %u", instance);
+ mgmt_print_adv_flags(flags);
+}
+
+static void mgmt_get_advertising_size_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+ uint32_t flags = get_le32(data + 1);
+ uint8_t adv_data_len = get_u8(data + 5);
+ uint8_t scan_rsp_len = get_u8(data + 6);
+
+ print_field("Instance: %u", instance);
+ mgmt_print_adv_flags(flags);
+ print_field("Advertising data length: %u", adv_data_len);
+ print_field("Scan response length: %u", scan_rsp_len);
+}
+
+static void mgmt_start_limited_discovery_cmd(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_start_limited_discovery_rsp(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+
+ mgmt_print_address_type(type);
+}
+
+static void mgmt_read_ext_controller_info_rsp(const void *data, uint16_t size)
+{
+ uint8_t version = get_u8(data + 6);
+ uint16_t manufacturer = get_le16(data + 7);
+ uint32_t supported_settings = get_le32(data + 9);
+ uint32_t current_settings = get_le32(data + 13);
+ uint16_t data_len = get_le16(data + 17);
+
+ print_addr_resolve("Address", data, 0x00, false);
+ mgmt_print_version(version);
+ mgmt_print_manufacturer(manufacturer);
+ mgmt_print_settings("Supported settings", supported_settings);
+ mgmt_print_settings("Current settings", current_settings);
+ print_field("Data length: %u", data_len);
+ print_eir(data + 19, size - 19, false);
+}
+
+static void mgmt_set_apperance_cmd(const void *data, uint16_t size)
+{
+ uint16_t appearance = get_le16(data);
+
+ print_appearance(appearance);
+}
+
+struct mgmt_data {
+ uint16_t opcode;
+ const char *str;
+ void (*func) (const void *data, uint16_t size);
+ uint16_t size;
+ bool fixed;
+ void (*rsp_func) (const void *data, uint16_t size);
+ uint16_t rsp_size;
+ bool rsp_fixed;
+};
+
+static const struct mgmt_data mgmt_command_table[] = {
+ { 0x0001, "Read Management Version Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_version_info_rsp, 3, true },
+ { 0x0002, "Read Management Supported Commands",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_supported_commands_rsp, 4, false },
+ { 0x0003, "Read Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_index_list_rsp, 2, false },
+ { 0x0004, "Read Controller Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_controller_info_rsp, 280, true },
+ { 0x0005, "Set Powered",
+ mgmt_set_powered_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0006, "Set Discoverable",
+ mgmt_set_discoverable_cmd, 3, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0007, "Set Connectable",
+ mgmt_set_connectable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0008, "Set Fast Connectable",
+ mgmt_set_fast_connectable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0009, "Set Bondable",
+ mgmt_set_bondable_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000a, "Set Link Security",
+ mgmt_set_link_security_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000b, "Set Secure Simple Pairing",
+ mgmt_set_secure_simple_pairing_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000c, "Set High Speed",
+ mgmt_set_high_speed_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000d, "Set Low Energy",
+ mgmt_set_low_energy_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x000e, "Set Device Class",
+ mgmt_set_device_class_cmd, 2, true,
+ mgmt_set_device_class_rsp, 3, true },
+ { 0x000f, "Set Local Name",
+ mgmt_set_local_name_cmd, 260, true,
+ mgmt_set_local_name_rsp, 260, true },
+ { 0x0010, "Add UUID",
+ mgmt_add_uuid_cmd, 17, true,
+ mgmt_add_uuid_rsp, 3, true },
+ { 0x0011, "Remove UUID",
+ mgmt_remove_uuid_cmd, 16, true,
+ mgmt_remove_uuid_rsp, 3, true },
+ { 0x0012, "Load Link Keys",
+ mgmt_load_link_keys_cmd, 3, false,
+ mgmt_null_rsp, 0, true },
+ { 0x0013, "Load Long Term Keys",
+ mgmt_load_long_term_keys_cmd, 2, false,
+ mgmt_null_rsp, 0, true },
+ { 0x0014, "Disconnect",
+ mgmt_disconnect_cmd, 7, true,
+ mgmt_disconnect_rsp, 7, true },
+ { 0x0015, "Get Connections",
+ mgmt_null_cmd, 0, true,
+ mgmt_get_connections_rsp, 2, false },
+ { 0x0016, "PIN Code Reply",
+ mgmt_pin_code_reply_cmd, 24, true,
+ mgmt_pin_code_reply_rsp, 7, true },
+ { 0x0017, "PIN Code Negative Reply",
+ mgmt_pin_code_neg_reply_cmd, 7, true,
+ mgmt_pin_code_neg_reply_rsp, 7, true },
+ { 0x0018, "Set IO Capability",
+ mgmt_set_io_capability_cmd, 1, true,
+ mgmt_null_rsp, 0, true },
+ { 0x0019, "Pair Device",
+ mgmt_pair_device_cmd, 8, true,
+ mgmt_pair_device_rsp, 7, true },
+ { 0x001a, "Cancel Pair Device",
+ mgmt_cancel_pair_device_cmd, 7, true,
+ mgmt_cancel_pair_device_rsp, 7, true },
+ { 0x001b, "Unpair Device",
+ mgmt_unpair_device_cmd, 8, true,
+ mgmt_unpair_device_rsp, 7, true },
+ { 0x001c, "User Confirmation Reply",
+ mgmt_user_confirmation_reply_cmd, 7, true,
+ mgmt_user_confirmation_reply_rsp, 7, true },
+ { 0x001d, "User Confirmation Negative Reply",
+ mgmt_user_confirmation_neg_reply_cmd, 7, true,
+ mgmt_user_confirmation_neg_reply_rsp, 7, true },
+ { 0x001e, "User Passkey Reply",
+ mgmt_user_passkey_reply_cmd, 11, true,
+ mgmt_user_passkey_reply_rsp, 7, true },
+ { 0x001f, "User Passkey Negative Reply",
+ mgmt_user_passkey_neg_reply_cmd, 7, true,
+ mgmt_user_passkey_neg_reply_rsp, 7, true },
+ { 0x0020, "Read Local Out Of Band Data",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_local_oob_data_rsp, 64, true },
+ { 0x0021, "Add Remote Out Of Band Data",
+ mgmt_add_remote_oob_data_cmd, 71, true,
+ mgmt_add_remote_oob_data_rsp, 7, true },
+ { 0x0022, "Remove Remote Out Of Band Data",
+ mgmt_remove_remote_oob_data_cmd, 7, true,
+ mgmt_remove_remote_oob_data_rsp, 7, true },
+ { 0x0023, "Start Discovery",
+ mgmt_start_discovery_cmd, 1, true,
+ mgmt_start_discovery_rsp, 1, true },
+ { 0x0024, "Stop Discovery",
+ mgmt_stop_discovery_cmd, 1, true,
+ mgmt_stop_discovery_rsp, 1, true },
+ { 0x0025, "Confirm Name",
+ mgmt_confirm_name_cmd, 8, true,
+ mgmt_confirm_name_rsp, 7, true },
+ { 0x0026, "Block Device",
+ mgmt_block_device_cmd, 7, true,
+ mgmt_block_device_rsp, 7, true },
+ { 0x0027, "Unblock Device",
+ mgmt_unblock_device_cmd, 7, true,
+ mgmt_unblock_device_rsp, 7, true },
+ { 0x0028, "Set Device ID",
+ mgmt_set_device_id_cmd, 8, true,
+ mgmt_null_rsp, 0, true },
+ { 0x0029, "Set Advertising",
+ mgmt_set_advertising_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002a, "Set BR/EDR",
+ mgmt_set_bredr_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002b, "Set Static Address",
+ mgmt_set_static_address_cmd, 6, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002c, "Set Scan Parameters",
+ mgmt_set_scan_parameters_cmd, 4, true,
+ mgmt_null_rsp, 0, true },
+ { 0x002d, "Set Secure Connections",
+ mgmt_set_secure_connections_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002e, "Set Debug Keys",
+ mgmt_set_debug_keys_cmd, 1, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x002f, "Set Privacy",
+ mgmt_set_privacy_cmd, 17, true,
+ mgmt_new_settings_rsp, 4, true },
+ { 0x0030, "Load Identity Resolving Keys",
+ mgmt_load_identity_resolving_keys_cmd, 2, false,
+ mgmt_null_rsp, 0, true },
+ { 0x0031, "Get Connection Information",
+ mgmt_get_connection_information_cmd, 7, true,
+ mgmt_get_connection_information_rsp, 10, true },
+ { 0x0032, "Get Clock Information",
+ mgmt_get_clock_information_cmd, 7, true,
+ mgmt_get_clock_information_rsp, 17, true },
+ { 0x0033, "Add Device",
+ mgmt_add_device_cmd, 8, true,
+ mgmt_add_device_rsp, 7, true },
+ { 0x0034, "Remove Device",
+ mgmt_remove_device_cmd, 7, true,
+ mgmt_remove_device_rsp, 7, true },
+ { 0x0035, "Load Connection Parameters",
+ mgmt_load_connection_parameters_cmd, 2, false,
+ mgmt_null_rsp, 0, true },
+ { 0x0036, "Read Unconfigured Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_unconf_index_list_rsp, 2, false },
+ { 0x0037, "Read Controller Configuration Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_controller_conf_info_rsp, 10, true },
+ { 0x0038, "Set External Configuration",
+ mgmt_set_external_configuration_cmd, 1, true,
+ mgmt_new_options_rsp, 4, true },
+ { 0x0039, "Set Public Address",
+ mgmt_set_public_address_cmd, 6, true,
+ mgmt_new_options_rsp, 4, true },
+ { 0x003a, "Start Service Discovery",
+ mgmt_start_service_discovery_cmd, 3, false,
+ mgmt_start_service_discovery_rsp, 1, true },
+ { 0x003b, "Read Local Out Of Band Extended Data",
+ mgmt_read_local_oob_ext_data_cmd, 1, true,
+ mgmt_read_local_oob_ext_data_rsp, 3, false },
+ { 0x003c, "Read Extended Controller Index List",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_ext_index_list_rsp, 2, false },
+ { 0x003d, "Read Advertising Features",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_advertising_features_rsp, 8, false },
+ { 0x003e, "Add Advertising",
+ mgmt_add_advertising_cmd, 11, false,
+ mgmt_add_advertising_rsp, 1, true },
+ { 0x003f, "Remove Advertising",
+ mgmt_remove_advertising_cmd, 1, true,
+ mgmt_remove_advertising_rsp, 1, true },
+ { 0x0040, "Get Advertising Size Information",
+ mgmt_get_advertising_size_info_cmd, 5, true,
+ mgmt_get_advertising_size_info_rsp, 7, true },
+ { 0x0041, "Start Limited Discovery",
+ mgmt_start_limited_discovery_cmd, 1, true,
+ mgmt_start_limited_discovery_rsp, 1, true },
+ { 0x0042, "Read Extended Controller Information",
+ mgmt_null_cmd, 0, true,
+ mgmt_read_ext_controller_info_rsp, 19, false },
+ { 0x0043, "Set Appearance",
+ mgmt_set_apperance_cmd, 2, true,
+ mgmt_null_rsp, 0, true },
+ { }
+};
+
+static void mgmt_null_evt(const void *data, uint16_t size)
+{
+}
+
+static void mgmt_command_complete_evt(const void *data, uint16_t size)
+{
+ uint16_t opcode;
+ uint8_t status;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ int i;
+
+ opcode = get_le16(data);
+ status = get_u8(data + 2);
+
+ data += 3;
+ size -= 3;
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ if (mgmt_data->rsp_func)
+ mgmt_color = COLOR_CTRL_COMMAND;
+ else
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+ " (0x%4.4x) plen %u", opcode, size);
+
+ mgmt_print_status(status);
+
+ if (!mgmt_data || !mgmt_data->rsp_func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->rsp_fixed) {
+ if (size != mgmt_data->rsp_size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->rsp_size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->rsp_func(data, size);
+}
+
+static void mgmt_command_status_evt(const void *data, uint16_t size)
+{
+ uint16_t opcode;
+ uint8_t status;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ int i;
+
+ opcode = get_le16(data);
+ status = get_u8(data + 2);
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ mgmt_color = COLOR_CTRL_COMMAND;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ print_indent(6, mgmt_color, "", mgmt_str, COLOR_OFF,
+ " (0x%4.4x)", opcode);
+
+ mgmt_print_status(status);
+}
+
+static void mgmt_controller_error_evt(const void *data, uint16_t size)
+{
+ uint8_t error = get_u8(data);
+
+ print_field("Error: 0x%2.2x", error);
+}
+
+static void mgmt_new_settings_evt(const void *data, uint16_t size)
+{
+ uint32_t settings = get_le32(data);
+
+ mgmt_print_settings("Current settings", settings);
+}
+
+static void mgmt_class_of_dev_changed_evt(const void *data, uint16_t size)
+{
+ print_dev_class(data);
+}
+
+static void mgmt_local_name_changed_evt(const void *data, uint16_t size)
+{
+ mgmt_print_name(data);
+}
+
+static void mgmt_new_link_key_evt(const void *data, uint16_t size)
+{
+ uint8_t store_hint = get_u8(data);
+
+ mgmt_print_store_hint(store_hint);
+ mgmt_print_link_key(data + 1);
+}
+
+static void mgmt_new_long_term_key_evt(const void *data, uint16_t size)
+{
+ uint8_t store_hint = get_u8(data);
+
+ mgmt_print_store_hint(store_hint);
+ mgmt_print_long_term_key(data + 1);
+}
+
+static void mgmt_device_connected_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint32_t flags = get_le32(data + 7);
+ uint16_t data_len = get_le16(data + 11);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_device_flags(flags);
+ print_field("Data length: %u", data_len);
+ print_eir(data + 13, size - 13, false);
+}
+
+static void mgmt_device_disconnected_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t reason = get_u8(data + 7);
+ const char *str;
+
+ mgmt_print_address(data, address_type);
+
+ switch (reason) {
+ case 0x00:
+ str = "Unspecified";
+ break;
+ case 0x01:
+ str = "Connection timeout";
+ break;
+ case 0x02:
+ str = "Connection terminated by local host";
+ break;
+ case 0x03:
+ str = "Connection terminated by remote host";
+ break;
+ case 0x04:
+ str = "Connection terminated due to authentication failure";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Reason: %s (0x%2.2x)", str, reason);
+}
+
+static void mgmt_connect_failed_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t status = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_status(status);
+}
+
+static void mgmt_pin_code_request_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t secure_pin = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ print_field("Secure PIN: 0x%2.2x", secure_pin);
+}
+
+static void mgmt_user_confirmation_request_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t confirm_hint = get_u8(data + 7);
+ uint32_t value = get_le32(data + 8);
+
+ mgmt_print_address(data, address_type);
+ print_field("Confirm hint: 0x%2.2x", confirm_hint);
+ print_field("Value: 0x%8.8x", value);
+}
+
+static void mgmt_user_passkey_request_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_authentication_failed_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t status = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_status(status);
+}
+
+static void mgmt_device_found_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ int8_t rssi = get_s8(data + 7);
+ uint32_t flags = get_le32(data + 8);
+ uint16_t data_len = get_le16(data + 12);
+
+ mgmt_print_address(data, address_type);
+ print_rssi(rssi);
+ mgmt_print_device_flags(flags);
+ print_field("Data length: %u", data_len);
+ print_eir(data + 14, size - 14, false);
+}
+
+static void mgmt_discovering_evt(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ uint8_t enable = get_u8(data + 1);
+
+ mgmt_print_address_type(type);
+ mgmt_print_enable("Discovery", enable);
+}
+
+static void mgmt_device_blocked_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_device_unblocked_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_device_unpaired_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_passkey_notify_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint32_t passkey = get_le32(data + 7);
+ uint8_t entered = get_u8(data + 11);
+
+ mgmt_print_address(data, address_type);
+ print_field("Passkey: 0x%8.8x", passkey);
+ print_field("Entered: %u", entered);
+}
+
+static void mgmt_new_identity_resolving_key_evt(const void *data, uint16_t size)
+{
+ uint8_t store_hint = get_u8(data);
+
+ mgmt_print_store_hint(store_hint);
+ print_addr_resolve("Random address", data + 1, 0x01, false);
+ mgmt_print_identity_resolving_key(data + 7);
+}
+
+static void mgmt_new_signature_resolving_key_evt(const void *data, uint16_t size)
+{
+ uint8_t store_hint = get_u8(data);
+
+ mgmt_print_store_hint(store_hint);
+ mgmt_print_signature_resolving_key(data + 1);
+}
+
+static void mgmt_device_added_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+ uint8_t action = get_u8(data + 7);
+
+ mgmt_print_address(data, address_type);
+ mgmt_print_device_action(action);
+}
+
+static void mgmt_device_removed_evt(const void *data, uint16_t size)
+{
+ uint8_t address_type = get_u8(data + 6);
+
+ mgmt_print_address(data, address_type);
+}
+
+static void mgmt_new_connection_parameter_evt(const void *data, uint16_t size)
+{
+ uint8_t store_hint = get_u8(data);
+
+ mgmt_print_store_hint(store_hint);
+ mgmt_print_connection_parameter(data + 1);
+}
+
+static void mgmt_new_conf_options_evt(const void *data, uint16_t size)
+{
+ uint32_t missing_options = get_le32(data);
+
+ mgmt_print_options("Missing options", missing_options);
+}
+
+static void mgmt_ext_index_added_evt(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ uint8_t bus = get_u8(data + 1);
+
+ print_field("type 0x%2.2x - bus 0x%2.2x", type, bus);
+}
+
+static void mgmt_ext_index_removed_evt(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ uint8_t bus = get_u8(data + 1);
+
+ print_field("type 0x%2.2x - bus 0x%2.2x", type, bus);
+}
+
+static void mgmt_local_oob_ext_data_updated_evt(const void *data, uint16_t size)
+{
+ uint8_t type = get_u8(data);
+ uint16_t data_len = get_le16(data + 1);
+
+ mgmt_print_address_type(type);
+ print_field("Data length: %u", data_len);
+ print_eir(data + 3, size - 3, true);
+}
+
+static void mgmt_advertising_added_evt(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+
+ print_field("Instance: %u", instance);
+}
+
+static void mgmt_advertising_removed_evt(const void *data, uint16_t size)
+{
+ uint8_t instance = get_u8(data);
+
+ print_field("Instance: %u", instance);
+}
+
+static void mgmt_ext_controller_info_changed_evt(const void *data, uint16_t size)
+{
+ uint16_t data_len = get_le16(data);
+
+ print_field("Data length: %u", data_len);
+ print_eir(data + 2, size - 2, false);
+}
+
+static const struct mgmt_data mgmt_event_table[] = {
+ { 0x0001, "Command Complete",
+ mgmt_command_complete_evt, 3, false },
+ { 0x0002, "Command Status",
+ mgmt_command_status_evt, 3, true },
+ { 0x0003, "Controller Error",
+ mgmt_controller_error_evt, 1, true },
+ { 0x0004, "Index Added",
+ mgmt_null_evt, 0, true },
+ { 0x0005, "Index Removed",
+ mgmt_null_evt, 0, true },
+ { 0x0006, "New Settings",
+ mgmt_new_settings_evt, 4, true },
+ { 0x0007, "Class Of Device Changed",
+ mgmt_class_of_dev_changed_evt, 3, true },
+ { 0x0008, "Local Name Changed",
+ mgmt_local_name_changed_evt, 260, true },
+ { 0x0009, "New Link Key",
+ mgmt_new_link_key_evt, 26, true },
+ { 0x000a, "New Long Term Key",
+ mgmt_new_long_term_key_evt, 37, true },
+ { 0x000b, "Device Connected",
+ mgmt_device_connected_evt, 13, false },
+ { 0x000c, "Device Disconnected",
+ mgmt_device_disconnected_evt, 8, true },
+ { 0x000d, "Connect Failed",
+ mgmt_connect_failed_evt, 8, true },
+ { 0x000e, "PIN Code Request",
+ mgmt_pin_code_request_evt, 8, true },
+ { 0x000f, "User Confirmation Request",
+ mgmt_user_confirmation_request_evt, 12, true },
+ { 0x0010, "User Passkey Request",
+ mgmt_user_passkey_request_evt, 7, true },
+ { 0x0011, "Authentication Failed",
+ mgmt_authentication_failed_evt, 8, true },
+ { 0x0012, "Device Found",
+ mgmt_device_found_evt, 14, false },
+ { 0x0013, "Discovering",
+ mgmt_discovering_evt, 2, true },
+ { 0x0014, "Device Blocked",
+ mgmt_device_blocked_evt, 7, true },
+ { 0x0015, "Device Unblocked",
+ mgmt_device_unblocked_evt, 7, true },
+ { 0x0016, "Device Unpaired",
+ mgmt_device_unpaired_evt, 7, true },
+ { 0x0017, "Passkey Notify",
+ mgmt_passkey_notify_evt, 12, true },
+ { 0x0018, "New Identity Resolving Key",
+ mgmt_new_identity_resolving_key_evt, 30, true },
+ { 0x0019, "New Signature Resolving Key",
+ mgmt_new_signature_resolving_key_evt, 25, true },
+ { 0x001a, "Device Added",
+ mgmt_device_added_evt, 8, true },
+ { 0x001b, "Device Removed",
+ mgmt_device_removed_evt, 7, true },
+ { 0x001c, "New Connection Parameter",
+ mgmt_new_connection_parameter_evt, 16, true },
+ { 0x001d, "Unconfigured Index Added",
+ mgmt_null_evt, 0, true },
+ { 0x001e, "Unconfigured Index Removed",
+ mgmt_null_evt, 0, true },
+ { 0x001f, "New Configuration Options",
+ mgmt_new_conf_options_evt, 4, true },
+ { 0x0020, "Extended Index Added",
+ mgmt_ext_index_added_evt, 2, true },
+ { 0x0021, "Extended Index Removed",
+ mgmt_ext_index_removed_evt, 2, true },
+ { 0x0022, "Local Out Of Band Extended Data Updated",
+ mgmt_local_oob_ext_data_updated_evt, 3, false },
+ { 0x0023, "Advertising Added",
+ mgmt_advertising_added_evt, 1, true },
+ { 0x0024, "Advertising Removed",
+ mgmt_advertising_removed_evt, 1, true },
+ { 0x0025, "Extended Controller Information Changed",
+ mgmt_ext_controller_info_changed_evt, 2, false },
+ { }
+};
+
+static void mgmt_print_commands(const void *data, uint16_t num)
+{
+ int i;
+
+ print_field("Commands: %u", num);
+
+ for (i = 0; i < num; i++) {
+ uint16_t opcode = get_le16(data + (i * 2));
+ const char *str = NULL;
+ int n;
+
+ for (n = 0; mgmt_command_table[n].str; n++) {
+ if (mgmt_command_table[n].opcode == opcode) {
+ str = mgmt_command_table[n].str;
+ break;
+ }
+ }
+
+ print_field(" %s (0x%4.4x)", str ?: "Reserved", opcode);
+ }
+}
+
+static void mgmt_print_events(const void *data, uint16_t num)
+{
+ int i;
+
+ print_field("Events: %u", num);
+
+ for (i = 0; i < num; i++) {
+ uint16_t opcode = get_le16(data + (i * 2));
+ const char *str = NULL;
+ int n;
+
+ for (n = 0; mgmt_event_table[n].str; n++) {
+ if (mgmt_event_table[n].opcode == opcode) {
+ str = mgmt_event_table[n].str;
+ break;
+ }
+ }
+
+ print_field(" %s (0x%4.4x)", str ?: "Reserved", opcode);
+ }
+}
+
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format, opcode;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ char channel[11], extra_str[25];
+ int i;
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Command packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ format = get_format(cookie);
+
+ if (format != CTRL_MGMT) {
+ char label[7];
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "Control Command", label, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (size < 2) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed MGMT Command packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ opcode = get_le16(data);
+
+ data += 2;
+ size -= 2;
+
+ for (i = 0; mgmt_command_table[i].str; i++) {
+ if (mgmt_command_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_command_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ if (mgmt_data->func)
+ mgmt_color = COLOR_CTRL_COMMAND;
+ else
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_COMMAND_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+ print_packet(tv, cred, '@', index, channel, mgmt_color,
+ "MGMT Command", mgmt_str, extra_str);
+
+ if (!mgmt_data || !mgmt_data->func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->fixed) {
+ if (size != mgmt_data->size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->func(data, size);
+}
+
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size)
+{
+ uint32_t cookie;
+ uint16_t format, opcode;
+ const struct mgmt_data *mgmt_data = NULL;
+ const char *mgmt_color, *mgmt_str;
+ char channel[11], extra_str[25];
+ int i;
+
+ if (size < 4) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed Control Event packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ cookie = get_le32(data);
+
+ data += 4;
+ size -= 4;
+
+ sprintf(channel, "0x%4.4x", cookie);
+
+ format = get_format(cookie);
+
+ if (format != CTRL_MGMT) {
+ char label[7];
+
+ sprintf(label, "0x%4.4x", format);
+
+ print_packet(tv, cred, '@', index, channel, COLOR_CTRL_CLOSE,
+ "Control Event", label, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (size < 2) {
+ print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
+ "Malformed MGMT Event packet", NULL, NULL);
+ packet_hexdump(data, size);
+ return;
+ }
+
+ opcode = get_le16(data);
+
+ data += 2;
+ size -= 2;
+
+ for (i = 0; mgmt_event_table[i].str; i++) {
+ if (mgmt_event_table[i].opcode == opcode) {
+ mgmt_data = &mgmt_event_table[i];
+ break;
+ }
+ }
+
+ if (mgmt_data) {
+ if (mgmt_data->func)
+ mgmt_color = COLOR_CTRL_EVENT;
+ else
+ mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+ mgmt_str = mgmt_data->str;
+ } else {
+ mgmt_color = COLOR_CTRL_EVENT_UNKNOWN;
+ mgmt_str = "Unknown";
+ }
+
+ sprintf(extra_str, "(0x%4.4x) plen %d", opcode, size);
+
+ print_packet(tv, cred, '@', index, channel, mgmt_color,
+ "MGMT Event", mgmt_str, extra_str);
+
+ if (!mgmt_data || !mgmt_data->func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (mgmt_data->fixed) {
+ if (size != mgmt_data->size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < mgmt_data->size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ mgmt_data->func(data, size);
+}
+
void packet_todo(void)
{
int i;
diff --git a/monitor/packet.h b/monitor/packet.h
index 30dfe38..a02ea50 100644
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -90,4 +90,13 @@
void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
bool in, const void *data, uint16_t size);
+void packet_ctrl_open(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_close(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_command(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
+ const void *data, uint16_t size);
+
void packet_todo(void);
diff --git a/monitor/uuid.h b/monitor/uuid.h
index 6ffc0ee..22d2363 100644
--- a/monitor/uuid.h
+++ b/monitor/uuid.h
@@ -24,6 +24,8 @@
#include <stdint.h>
+#define MAX_LEN_UUID_STR 37
+
const char *uuid16_to_str(uint16_t uuid);
const char *uuid32_to_str(uint32_t uuid);
const char *uuidstr_to_str(const char *uuid);
diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c
index be007de..e35124a 100644
--- a/obexd/client/bluetooth.c
+++ b/obexd/client/bluetooth.c
@@ -443,8 +443,8 @@
{
int sk = g_io_channel_unix_get_fd(io);
int type;
- int omtu = -1;
- int imtu = -1;
+ uint16_t omtu = BT_TX_MTU;
+ uint16_t imtu = BT_RX_MTU;
socklen_t len = sizeof(int);
DBG("");
diff --git a/obexd/plugins/bluetooth.c b/obexd/plugins/bluetooth.c
index d8b872a..3ee5432 100644
--- a/obexd/plugins/bluetooth.c
+++ b/obexd/plugins/bluetooth.c
@@ -75,8 +75,8 @@
struct bluetooth_profile *profile = user_data;
struct obex_server *server = profile->server;
int type;
- int omtu = BT_TX_MTU;
- int imtu = BT_RX_MTU;
+ uint16_t omtu = BT_TX_MTU;
+ uint16_t imtu = BT_RX_MTU;
gboolean stream = TRUE;
socklen_t len = sizeof(int);
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index bab691c..ad93208 100644
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
@@ -543,13 +543,18 @@
} else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) {
/* Always relative */
- if (!name || strlen(name) == 0)
+ if (!name || strlen(name) == 0) {
/* Current folder */
path = g_strdup(pbap->folder);
- else
+ } else {
/* Current folder + relative path */
path = g_build_filename(pbap->folder, name, NULL);
+ /* clear cache */
+ pbap->cache.valid = FALSE;
+ pbap->cache.index = 0;
+ cache_clear(&pbap->cache);
+ }
} else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) {
/* File name only */
path = g_strdup(name);
diff --git a/plugins/policy.c b/plugins/policy.c
index 218a3ed..c9a7c84 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -95,10 +95,29 @@
uint8_t tg_retries;
};
+static struct reconnect_data *reconnect_find(struct btd_device *dev)
+{
+ GSList *l;
+
+ for (l = reconnects; l; l = g_slist_next(l)) {
+ struct reconnect_data *reconnect = l->data;
+
+ if (reconnect->dev == dev)
+ return reconnect;
+ }
+
+ return NULL;
+}
+
static void policy_connect(struct policy_data *data,
struct btd_service *service)
{
struct btd_profile *profile = btd_service_get_profile(service);
+ struct reconnect_data *reconnect;
+
+ reconnect = reconnect_find(btd_service_get_device(service));
+ if (reconnect && reconnect->active)
+ return;
DBG("%s profile %s", device_get_path(data->dev), profile->name);
@@ -496,6 +515,7 @@
static void reconnect_reset(struct reconnect_data *reconnect)
{
reconnect->attempt = 0;
+ reconnect->active = false;
if (reconnect->timer > 0) {
g_source_remove(reconnect->timer);
@@ -518,20 +538,6 @@
return false;
}
-static struct reconnect_data *reconnect_find(struct btd_device *dev)
-{
- GSList *l;
-
- for (l = reconnects; l; l = g_slist_next(l)) {
- struct reconnect_data *reconnect = l->data;
-
- if (reconnect->dev == dev)
- return reconnect;
- }
-
- return NULL;
-}
-
static struct reconnect_data *reconnect_add(struct btd_service *service)
{
struct btd_device *dev = btd_service_get_device(service);
@@ -643,7 +649,6 @@
*/
reconnect = reconnect_add(service);
- reconnect->active = false;
reconnect_reset(reconnect);
/*
@@ -675,7 +680,6 @@
return FALSE;
}
- reconnect->active = true;
reconnect->attempt++;
return FALSE;
@@ -685,12 +689,13 @@
{
static int timeout = 0;
- reconnect->attempt++;
+ reconnect->active = true;
if (reconnect->attempt < reconnect_intervals_len)
timeout = reconnect_intervals[reconnect->attempt];
- DBG("%d seconds", timeout);
+ DBG("attempt %u/%zu %d seconds", reconnect->attempt + 1,
+ reconnect_attempts, timeout);
reconnect->timer = g_timeout_add_seconds(timeout, reconnect_timeout,
reconnect);
@@ -709,6 +714,8 @@
if (!reconnect || !reconnect->reconnect)
return;
+ reconnect_reset(reconnect);
+
DBG("Device %s identified for auto-reconnection",
device_get_path(dev));
@@ -728,8 +735,6 @@
if (!reconnect->active)
return;
- reconnect->active = false;
-
/* Give up if we were powered off */
if (status == MGMT_STATUS_NOT_POWERED) {
reconnect_reset(reconnect);
@@ -777,6 +782,8 @@
goto done;
}
+ g_key_file_set_list_separator(conf, ',');
+
reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
"ReconnectUUIDs",
NULL, &gerr);
diff --git a/profiles/alert/server.c b/profiles/alert/server.c
deleted file mode 100644
index 2f6e3cd..0000000
--- a/profiles/alert/server.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-/*
- *
- * 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 <stdbool.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/hci.h"
-#include "lib/hci_lib.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/dbus-common.h"
-#include "attrib/att.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "attrib/att-database.h"
-#include "src/log.h"
-#include "attrib/gatt-service.h"
-#include "attrib/gattrib.h"
-#include "src/attrib-server.h"
-#include "attrib/gatt.h"
-#include "src/profile.h"
-#include "src/error.h"
-#include "src/textfile.h"
-#include "src/attio.h"
-
-#define PHONE_ALERT_STATUS_SVC_UUID 0x180E
-#define ALERT_NOTIF_SVC_UUID 0x1811
-
-#define ALERT_STATUS_CHR_UUID 0x2A3F
-#define RINGER_CP_CHR_UUID 0x2A40
-#define RINGER_SETTING_CHR_UUID 0x2A41
-
-#define ALERT_NOTIF_CP_CHR_UUID 0x2A44
-#define UNREAD_ALERT_CHR_UUID 0x2A45
-#define NEW_ALERT_CHR_UUID 0x2A46
-#define SUPP_NEW_ALERT_CAT_CHR_UUID 0x2A47
-#define SUPP_UNREAD_ALERT_CAT_CHR_UUID 0x2A48
-
-#define ALERT_OBJECT_PATH "/org/bluez"
-#define ALERT_INTERFACE "org.bluez.Alert1"
-#define ALERT_AGENT_INTERFACE "org.bluez.AlertAgent1"
-
-/* Maximum length for "Text String Information" */
-#define NEW_ALERT_MAX_INFO_SIZE 18
-/* Maximum length for New Alert Characteristic Value */
-#define NEW_ALERT_CHR_MAX_VALUE_SIZE (NEW_ALERT_MAX_INFO_SIZE + 2)
-
-enum {
- ENABLE_NEW_INCOMING,
- ENABLE_UNREAD_CAT,
- DISABLE_NEW_INCOMING,
- DISABLE_UNREAD_CAT,
- NOTIFY_NEW_INCOMING,
- NOTIFY_UNREAD_CAT,
-};
-
-enum {
- RINGER_SILENT_MODE = 1,
- RINGER_MUTE_ONCE,
- RINGER_CANCEL_SILENT_MODE,
-};
-
-/* Ringer Setting characteristic values */
-enum {
- RINGER_SILENT,
- RINGER_NORMAL,
-};
-
-enum notify_type {
- NOTIFY_RINGER_SETTING = 0,
- NOTIFY_ALERT_STATUS,
- NOTIFY_NEW_ALERT,
- NOTIFY_UNREAD_ALERT,
- NOTIFY_SIZE,
-};
-
-struct alert_data {
- const char *category;
- char *srv;
- char *path;
- guint watcher;
-};
-
-struct alert_adapter {
- struct btd_adapter *adapter;
- uint16_t supp_new_alert_cat_handle;
- uint16_t supp_unread_alert_cat_handle;
- uint16_t hnd_ccc[NOTIFY_SIZE];
- uint16_t hnd_value[NOTIFY_SIZE];
-};
-
-struct notify_data {
- struct alert_adapter *al_adapter;
- enum notify_type type;
- uint8_t *value;
- size_t len;
-};
-
-struct notify_callback {
- struct notify_data *notify_data;
- struct btd_device *device;
- guint id;
-};
-
-static GSList *registered_alerts = NULL;
-static GSList *alert_adapters = NULL;
-static uint8_t ringer_setting = RINGER_NORMAL;
-static uint8_t alert_status = 0;
-
-static const char * const anp_categories[] = {
- "simple",
- "email",
- "news",
- "call",
- "missed-call",
- "sms-mms",
- "voice-mail",
- "schedule",
- "high-priority",
- "instant-message",
-};
-
-static const char * const pasp_categories[] = {
- "ringer",
- "vibrate",
- "display",
-};
-
-static int adapter_cmp(gconstpointer a, gconstpointer b)
-{
- const struct alert_adapter *al_adapter = a;
- const struct btd_adapter *adapter = b;
-
- return al_adapter->adapter == adapter ? 0 : -1;
-}
-
-static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter)
-{
- GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp);
-
- return l ? l->data : NULL;
-}
-
-static void alert_data_destroy(gpointer user_data)
-{
- struct alert_data *alert = user_data;
-
- if (alert->watcher)
- g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher);
-
- g_free(alert->srv);
- g_free(alert->path);
- g_free(alert);
-}
-
-static void alert_release(gpointer user_data)
-{
- struct alert_data *alert = user_data;
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(alert->srv, alert->path,
- ALERT_AGENT_INTERFACE,
- "Release");
- if (msg)
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-
- alert_data_destroy(alert);
-}
-
-static void alert_destroy(gpointer user_data)
-{
- DBG("");
-
- g_slist_free_full(registered_alerts, alert_release);
- registered_alerts = NULL;
-}
-
-static const char *valid_category(const char *category)
-{
- unsigned i;
-
- for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
- if (g_str_equal(anp_categories[i], category))
- return anp_categories[i];
- }
-
- for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
- if (g_str_equal(pasp_categories[i], category))
- return pasp_categories[i];
- }
-
- return NULL;
-}
-
-static struct alert_data *get_alert_data_by_category(const char *category)
-{
- GSList *l;
- struct alert_data *alert;
-
- for (l = registered_alerts; l; l = g_slist_next(l)) {
- alert = l->data;
- if (g_str_equal(alert->category, category))
- return alert;
- }
-
- return NULL;
-}
-
-static gboolean registered_category(const char *category)
-{
- struct alert_data *alert;
-
- alert = get_alert_data_by_category(category);
- if (alert)
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean pasp_category(const char *category)
-{
- unsigned i;
-
- for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++)
- if (g_str_equal(category, pasp_categories[i]))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean valid_description(const char *category,
- const char *description)
-{
- if (!pasp_category(category)) {
- if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE)
- return FALSE;
-
- return TRUE;
- }
-
- if (g_str_equal(description, "active") ||
- g_str_equal(description, "not active"))
- return TRUE;
-
- if (g_str_equal(category, "ringer"))
- if (g_str_equal(description, "enabled") ||
- g_str_equal(description, "disabled"))
- return TRUE;
-
- return FALSE;
-}
-
-static gboolean valid_count(const char *category, uint16_t count)
-{
- if (!pasp_category(category) && count > 0 && count <= 255)
- return TRUE;
-
- if (pasp_category(category) && count == 1)
- return TRUE;
-
- return FALSE;
-}
-
-static void update_supported_categories(gpointer data, gpointer user_data)
-{
- struct alert_adapter *al_adapter = data;
- struct btd_adapter *adapter = al_adapter->adapter;
- uint8_t value[2];
- unsigned int i;
-
- memset(value, 0, sizeof(value));
-
- for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
- if (registered_category(anp_categories[i]))
- hci_set_bit(i, value);
- }
-
- attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL,
- value, sizeof(value), NULL);
-
- /* FIXME: For now report all registered categories as supporting unread
- * status, until it is known which ones should be supported */
- attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle,
- NULL, value, sizeof(value), NULL);
-}
-
-static void watcher_disconnect(DBusConnection *conn, void *user_data)
-{
- struct alert_data *alert = user_data;
-
- DBG("Category %s was disconnected", alert->category);
-
- registered_alerts = g_slist_remove(registered_alerts, alert);
- alert_data_destroy(alert);
-
- g_slist_foreach(alert_adapters, update_supported_categories, NULL);
-}
-
-static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc)
-{
- char *filename;
- GKeyFile *key_file;
- char handle[6];
- char *str;
- uint16_t val;
- gboolean result;
-
- sprintf(handle, "%hu", ccc);
-
- filename = btd_device_get_storage_path(device, "ccc");
- if (!filename) {
- warn("Unable to get ccc storage path for device");
- return FALSE;
- }
-
- 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, handle, "Value", NULL);
- if (!str) {
- result = FALSE;
- goto end;
- }
-
- val = strtol(str, NULL, 16);
- if (!(val & 0x0001)) {
- result = FALSE;
- goto end;
- }
-
- result = TRUE;
-end:
- g_free(str);
- g_free(filename);
- g_key_file_free(key_file);
-
- return result;
-}
-
-static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct notify_callback *cb = user_data;
-
- DBG("status=%#x", status);
-
- btd_device_remove_attio_callback(cb->device, cb->id);
- btd_device_unref(cb->device);
- g_free(cb->notify_data->value);
- g_free(cb->notify_data);
- g_free(cb);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct notify_callback *cb = user_data;
- struct notify_data *nd = cb->notify_data;
- enum notify_type type = nd->type;
- struct alert_adapter *al_adapter = nd->al_adapter;
- size_t len;
- uint8_t *pdu = g_attrib_get_buffer(attrib, &len);
-
-
- switch (type) {
- case NOTIFY_RINGER_SETTING:
- len = enc_notification(al_adapter->hnd_value[type],
- &ringer_setting, sizeof(ringer_setting),
- pdu, len);
- break;
- case NOTIFY_ALERT_STATUS:
- len = enc_notification(al_adapter->hnd_value[type],
- &alert_status, sizeof(alert_status),
- pdu, len);
- break;
- case NOTIFY_NEW_ALERT:
- case NOTIFY_UNREAD_ALERT:
- len = enc_notification(al_adapter->hnd_value[type],
- nd->value, nd->len, pdu, len);
- break;
- case NOTIFY_SIZE:
- default:
- DBG("Unknown type, could not send notification");
- goto end;
- }
-
- DBG("Send notification for handle: 0x%04x, ccc: 0x%04x",
- al_adapter->hnd_value[type],
- al_adapter->hnd_ccc[type]);
-
- g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
-
- return;
-
-end:
- btd_device_remove_attio_callback(cb->device, cb->id);
- btd_device_unref(cb->device);
- g_free(cb->notify_data->value);
- g_free(cb->notify_data);
- g_free(cb);
-}
-
-static void filter_devices_notify(struct btd_device *device, void *user_data)
-{
- struct notify_data *notify_data = user_data;
- struct alert_adapter *al_adapter = notify_data->al_adapter;
- enum notify_type type = notify_data->type;
- struct notify_callback *cb;
-
- if (!is_notifiable_device(device, al_adapter->hnd_ccc[type]))
- return;
-
- cb = g_new0(struct notify_callback, 1);
- cb->notify_data = notify_data;
- cb->device = btd_device_ref(device);
- cb->id = btd_device_add_attio_callback(device,
- attio_connected_cb, NULL, cb);
-}
-
-static void notify_devices(struct alert_adapter *al_adapter,
- enum notify_type type, uint8_t *value, size_t len)
-{
- struct notify_data *notify_data;
-
- notify_data = g_new0(struct notify_data, 1);
- notify_data->al_adapter = al_adapter;
- notify_data->type = type;
- notify_data->value = g_memdup(value, len);
- notify_data->len = len;
-
- btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify,
- notify_data);
-}
-
-static void pasp_notification(enum notify_type type)
-{
- GSList *it;
- struct alert_adapter *al_adapter;
-
- for (it = alert_adapters; it; it = g_slist_next(it)) {
- al_adapter = it->data;
-
- notify_devices(al_adapter, type, NULL, 0);
- }
-}
-
-static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- char *path;
- const char *category;
- const char *c;
- struct alert_data *alert;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c,
- DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- category = valid_category(c);
- if (!category) {
- DBG("Invalid category: %s", c);
- return btd_error_invalid_args(msg);
- }
-
- if (registered_category(category)) {
- DBG("Category %s already registered", category);
- return dbus_message_new_method_return(msg);
- }
-
- alert = g_new0(struct alert_data, 1);
- alert->srv = g_strdup(sender);
- alert->path = g_strdup(path);
- alert->category = category;
- alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv,
- watcher_disconnect, alert, NULL);
-
- if (alert->watcher == 0) {
- alert_data_destroy(alert);
- DBG("Could not register disconnect watcher");
- return btd_error_failed(msg,
- "Could not register disconnect watcher");
- }
-
- registered_alerts = g_slist_append(registered_alerts, alert);
-
- g_slist_foreach(alert_adapters, update_supported_categories, NULL);
-
- DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path);
-
- return dbus_message_new_method_return(msg);
-}
-
-static void update_new_alert(gpointer data, gpointer user_data)
-{
- struct alert_adapter *al_adapter = data;
- struct btd_adapter *adapter = al_adapter->adapter;
- uint8_t *value = user_data;
-
- attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL,
- &value[1], value[0], NULL);
-
- notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]);
-}
-
-static void update_phone_alerts(const char *category, const char *description)
-{
- unsigned int i;
-
- if (g_str_equal(category, "ringer")) {
- if (g_str_equal(description, "enabled")) {
- ringer_setting = RINGER_NORMAL;
- pasp_notification(NOTIFY_RINGER_SETTING);
- return;
- } else if (g_str_equal(description, "disabled")) {
- ringer_setting = RINGER_SILENT;
- pasp_notification(NOTIFY_RINGER_SETTING);
- return;
- }
- }
-
- for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
- if (g_str_equal(pasp_categories[i], category)) {
- if (g_str_equal(description, "active")) {
- alert_status |= (1 << i);
- pasp_notification(NOTIFY_ALERT_STATUS);
- } else if (g_str_equal(description, "not active")) {
- alert_status &= ~(1 << i);
- pasp_notification(NOTIFY_ALERT_STATUS);
- }
- break;
- }
- }
-}
-
-static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- const char *category, *description;
- struct alert_data *alert;
- uint16_t count;
- unsigned int i;
- size_t dlen;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
- DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING,
- &description, DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- alert = get_alert_data_by_category(category);
- if (!alert) {
- DBG("Category %s not registered", category);
- return btd_error_invalid_args(msg);
- }
-
- if (!g_str_equal(alert->srv, sender)) {
- DBG("Sender %s is not registered in category %s", sender,
- category);
- return btd_error_invalid_args(msg);
- }
-
- if (!valid_description(category, description)) {
- DBG("Description %s is invalid for %s category",
- description, category);
- return btd_error_invalid_args(msg);
- }
-
- if (!valid_count(category, count)) {
- DBG("Count %d is invalid for %s category", count, category);
- return btd_error_invalid_args(msg);
- }
-
- dlen = strlen(description);
-
- for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
- uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1];
- uint8_t *ptr = value;
-
- if (!g_str_equal(anp_categories[i], category))
- continue;
-
- memset(value, 0, sizeof(value));
-
- *ptr++ = 2; /* Attribute value size */
- *ptr++ = i; /* Category ID (mandatory) */
- *ptr++ = count; /* Number of New Alert (mandatory) */
- /* Text String Information (optional) */
- strncpy((char *) ptr, description,
- NEW_ALERT_MAX_INFO_SIZE - 1);
-
- if (dlen > 0)
- *value += dlen + 1;
-
- g_slist_foreach(alert_adapters, update_new_alert, value);
- }
-
- if (pasp_category(category))
- update_phone_alerts(category, description);
-
- DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description);
-
- return dbus_message_new_method_return(msg);
-}
-
-static int agent_ringer_mute_once(void)
-{
- struct alert_data *alert;
- DBusMessage *msg;
-
- alert = get_alert_data_by_category("ringer");
- if (!alert) {
- DBG("Category ringer is not registered");
- return -EINVAL;
- }
-
- msg = dbus_message_new_method_call(alert->srv, alert->path,
- ALERT_AGENT_INTERFACE, "MuteOnce");
- if (!msg)
- return -ENOMEM;
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-
- return 0;
-}
-
-static int agent_ringer_set_ringer(const char *mode)
-{
- struct alert_data *alert;
- DBusMessage *msg;
-
- alert = get_alert_data_by_category("ringer");
- if (!alert) {
- DBG("Category ringer is not registered");
- return -EINVAL;
- }
-
- msg = dbus_message_new_method_call(alert->srv, alert->path,
- ALERT_AGENT_INTERFACE, "SetRinger");
- if (!msg)
- return -ENOMEM;
-
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode,
- DBUS_TYPE_INVALID);
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-
- return 0;
-}
-
-static void update_unread_alert(gpointer data, gpointer user_data)
-{
- struct alert_adapter *al_adapter = data;
- struct btd_adapter *adapter = al_adapter->adapter;
- uint8_t *value = user_data;
-
- attrib_db_update(adapter,
- al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value,
- 2, NULL);
-
- notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2);
-}
-
-static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct alert_data *alert;
- const char *category;
- unsigned int i;
- uint16_t count;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
- DBUS_TYPE_UINT16, &count,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- alert = get_alert_data_by_category(category);
- if (!alert) {
- DBG("Category %s not registered", category);
- return btd_error_invalid_args(msg);
- }
-
- if (!valid_count(category, count)) {
- DBG("Count %d is invalid for %s category", count, category);
- return btd_error_invalid_args(msg);
- }
-
- if (!g_str_equal(alert->srv, sender)) {
- DBG("Sender %s is not registered in category %s", sender,
- category);
- return btd_error_invalid_args(msg);
- }
-
- for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
- if (g_str_equal(anp_categories[i], category)) {
- uint8_t value[2];
-
- value[0] = i; /* Category ID */
- value[1] = count; /* Unread count */
-
- g_slist_foreach(alert_adapters, update_unread_alert,
- value);
- }
- }
-
- DBG("category %s, count %d", category, count);
-
- return dbus_message_new_method_return(msg);
-}
-
-static uint8_t ringer_cp_write(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- DBG("a = %p", a);
-
- if (a->len > 1) {
- DBG("Invalid command size (%zu)", a->len);
- return 0;
- }
-
- switch (a->data[0]) {
- case RINGER_SILENT_MODE:
- DBG("Silent Mode");
- agent_ringer_set_ringer("disabled");
- break;
- case RINGER_MUTE_ONCE:
- DBG("Mute Once");
- agent_ringer_mute_once();
- break;
- case RINGER_CANCEL_SILENT_MODE:
- DBG("Cancel Silent Mode");
- agent_ringer_set_ringer("enabled");
- break;
- default:
- DBG("Invalid command (0x%02x)", a->data[0]);
- }
-
- return 0;
-}
-
-static uint8_t alert_status_read(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
-
- DBG("a = %p", a);
-
- if (a->data == NULL || a->data[0] != alert_status)
- attrib_db_update(adapter, a->handle, NULL, &alert_status,
- sizeof(alert_status), NULL);
-
- return 0;
-}
-
-static uint8_t ringer_setting_read(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
-
- DBG("a = %p", a);
-
- if (a->data == NULL || a->data[0] != ringer_setting)
- attrib_db_update(adapter, a->handle, NULL, &ringer_setting,
- sizeof(ringer_setting), NULL);
-
- return 0;
-}
-
-static void register_phone_alert_service(struct alert_adapter *al_adapter)
-{
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID);
-
- /* Phone Alert Status Service */
- gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
- /* Alert Status characteristic */
- GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
- GATT_CHR_PROP_NOTIFY,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- alert_status_read, al_adapter->adapter,
- GATT_OPT_CCC_GET_HANDLE,
- &al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS],
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
- /* Ringer Control Point characteristic */
- GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
- ringer_cp_write, NULL,
- /* Ringer Setting characteristic */
- GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
- GATT_CHR_PROP_NOTIFY,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- ringer_setting_read, al_adapter->adapter,
- GATT_OPT_CCC_GET_HANDLE,
- &al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING],
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->hnd_value[NOTIFY_RINGER_SETTING],
- GATT_OPT_INVALID);
-}
-
-static uint8_t supp_new_alert_cat_read(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- uint8_t value[] = { 0x00, 0x00 };
-
- DBG("a = %p", a);
-
- if (a->data == NULL)
- attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
- NULL);
-
- return 0;
-}
-
-static uint8_t supp_unread_alert_cat_read(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- uint8_t value[] = { 0x00, 0x00 };
-
- DBG("a = %p", a);
-
- if (a->data == NULL)
- attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
- NULL);
-
- return 0;
-}
-
-static uint8_t alert_notif_cp_write(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- DBG("a = %p", a);
-
- if (a->len < 2)
- return 0;
-
- switch (a->data[0]) {
- case ENABLE_NEW_INCOMING:
- DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]);
- break;
- case ENABLE_UNREAD_CAT:
- DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]);
- break;
- case DISABLE_NEW_INCOMING:
- DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]);
- break;
- case DISABLE_UNREAD_CAT:
- DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]);
- break;
- case NOTIFY_NEW_INCOMING:
- DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]);
- break;
- case NOTIFY_UNREAD_CAT:
- DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]);
- break;
- default:
- DBG("0x%02x 0x%02x", a->data[0], a->data[1]);
- }
-
- return 0;
-}
-
-static void register_alert_notif_service(struct alert_adapter *al_adapter)
-{
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID);
-
- /* Alert Notification Service */
- gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
- /* Supported New Alert Category */
- GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- supp_new_alert_cat_read, al_adapter->adapter,
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->supp_new_alert_cat_handle,
- /* New Alert */
- GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
- GATT_OPT_CCC_GET_HANDLE,
- &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->hnd_value[NOTIFY_NEW_ALERT],
- /* Supported Unread Alert Category */
- GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- supp_unread_alert_cat_read, al_adapter->adapter,
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->supp_unread_alert_cat_handle,
- /* Unread Alert Status */
- GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
- GATT_OPT_CCC_GET_HANDLE,
- &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
- GATT_OPT_CHR_VALUE_GET_HANDLE,
- &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
- /* Alert Notification Control Point */
- GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
- alert_notif_cp_write, NULL,
- GATT_OPT_INVALID);
-}
-
-static int alert_server_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- struct alert_adapter *al_adapter;
-
- al_adapter = g_new0(struct alert_adapter, 1);
- al_adapter->adapter = btd_adapter_ref(adapter);
-
- alert_adapters = g_slist_append(alert_adapters, al_adapter);
-
- register_phone_alert_service(al_adapter);
- register_alert_notif_service(al_adapter);
-
- return 0;
-}
-
-static void alert_server_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- struct alert_adapter *al_adapter;
-
- al_adapter = find_alert_adapter(adapter);
- if (!al_adapter)
- return;
-
- alert_adapters = g_slist_remove(alert_adapters, al_adapter);
- btd_adapter_unref(al_adapter->adapter);
-
- g_free(al_adapter);
-}
-
-static struct btd_profile alert_profile = {
- .name = "gatt-alert-server",
- .adapter_probe = alert_server_probe,
- .adapter_remove = alert_server_remove,
-};
-
-static const GDBusMethodTable alert_methods[] = {
- { GDBUS_METHOD("RegisterAlert",
- GDBUS_ARGS({ "category", "s" },
- { "agent", "o" }), NULL,
- register_alert) },
- { GDBUS_METHOD("NewAlert",
- GDBUS_ARGS({ "category", "s" },
- { "count", "q" },
- { "description", "s" }), NULL,
- new_alert) },
- { GDBUS_METHOD("UnreadAlert",
- GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL,
- unread_alert) },
- { }
-};
-
-static int alert_server_init(void)
-{
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- ALERT_OBJECT_PATH, ALERT_INTERFACE,
- alert_methods, NULL, NULL, NULL,
- alert_destroy)) {
- error("D-Bus failed to register %s interface",
- ALERT_INTERFACE);
- return -EIO;
- }
-
- return btd_profile_register(&alert_profile);
-}
-
-static void alert_server_exit(void)
-{
- btd_profile_unregister(&alert_profile);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- ALERT_OBJECT_PATH, ALERT_INTERFACE);
-}
-
-static int alert_init(void)
-{
- return alert_server_init();
-}
-
-static void alert_exit(void)
-{
- alert_server_exit();
-}
-
-BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- alert_init, alert_exit)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index b391fc2..db0736d 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -235,11 +235,11 @@
if (avdtp_error_category(err) != AVDTP_ERRNO)
return -EIO;
- perr = -avdtp_error_posix_errno(err);
- switch (-perr) {
- case -EHOSTDOWN:
- case -ECONNABORTED:
- return perr;
+ perr = avdtp_error_posix_errno(err);
+ switch (perr) {
+ case EHOSTDOWN:
+ case ECONNABORTED:
+ return -perr;
default:
/*
* An unexpect error has occurred setup may be attempted again.
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index c100149..51a89b1 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -2793,6 +2793,83 @@
avrcp_player_value_rsp, session);
}
+static gboolean avrcp_set_addressed_player_rsp(struct avctp *conn, uint8_t code,
+ uint8_t subunit, uint8_t transaction,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp *session = user_data;
+ struct avrcp_player *player = session->controller->player;
+ struct avrcp_header *pdu = (void *) operands;
+
+ if (!pdu || code != AVC_CTYPE_ACCEPTED)
+ return FALSE;
+
+ player->addressed = true;
+
+ return FALSE;
+}
+
+static void avrcp_set_addressed_player(struct avrcp *session,
+ struct avrcp_player *player)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+ struct avrcp_header *pdu = (void *) buf;
+ uint16_t id;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_SET_ADDRESSED_PLAYER;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ id = htons(player->id);
+ memcpy(pdu->params, &id, 2);
+ pdu->params_len = htons(2);
+
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_set_addressed_player_rsp,
+ session);
+}
+
+static void set_addressed_player(struct avrcp *session,
+ struct avrcp_player *player)
+{
+ if (!player || !player->id || player->addressed ||
+ session->controller->version < 0x0104)
+ return;
+
+ /* Set player as addressed */
+ avrcp_set_addressed_player(session, player);
+}
+
+static void set_browsed_player(struct avrcp *session,
+ struct avrcp_player *player)
+{
+ if (!player || !player->id || player->browsed)
+ return;
+
+ if (media_player_get_browsable(player->user_data))
+ avrcp_set_browsed_player(session, player);
+}
+
+static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
+{
+ struct btd_service *service;
+
+ if (session->controller->player == player)
+ goto done;
+
+ session->controller->player = player;
+ service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+ control_set_player(service, player ?
+ media_player_get_path(player->user_data) : NULL);
+
+done:
+ set_addressed_player(session, player);
+ set_browsed_player(session, player);
+}
+
static bool ct_set_setting(struct media_player *mp, const char *key,
const char *value, void *user_data)
{
@@ -2808,6 +2885,8 @@
if (session->controller->version < 0x0103)
return false;
+ set_ct_player(session, player);
+
attr = attr_to_val(key);
if (attr < 0)
return false;
@@ -2830,6 +2909,8 @@
if (session == NULL)
return -ENOTCONN;
+ set_ct_player(session, player);
+
err = avctp_send_passthrough(session->conn, op);
if (err < 0)
return err;
@@ -2898,6 +2979,8 @@
session = player->sessions->data;
+ set_ct_player(session, player);
+
if (g_str_has_prefix(name, "/NowPlaying"))
player->scope = 0x03;
else if (g_str_has_suffix(name, "/search"))
@@ -2942,6 +3025,7 @@
uint8_t direction;
session = player->sessions->data;
+ set_ct_player(session, player);
player->change_path = g_strdup(path);
direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
@@ -3008,6 +3092,7 @@
session = player->sessions->data;
+ set_ct_player(session, player);
avrcp_search(session, string);
return 0;
@@ -3048,9 +3133,12 @@
return -EBUSY;
session = player->sessions->data;
+ set_ct_player(session, player);
if (g_strrstr(name, "/NowPlaying"))
player->scope = 0x03;
+ else if (g_strrstr(name, "/Search"))
+ player->scope = 0x02;
else
player->scope = 0x01;
@@ -3100,6 +3188,7 @@
else
player->scope = 0x01;
+ set_ct_player(session, player);
avrcp_add_to_nowplaying(session, uid);
return 0;
@@ -3159,6 +3248,7 @@
struct avrcp *session;
session = player->sessions->data;
+ set_ct_player(session, player);
if (session->controller->version != 0x0106) {
error("version not supported");
@@ -3194,16 +3284,6 @@
.total_items = ct_get_total_numberofitems,
};
-static void set_ct_player(struct avrcp *session, struct avrcp_player *player)
-{
- struct btd_service *service;
-
- session->controller->player = player;
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- control_set_player(service, player ?
- media_player_get_path(player->user_data) : NULL);
-}
-
static struct avrcp_player *create_ct_player(struct avrcp *session,
uint16_t id)
{
@@ -3212,6 +3292,7 @@
const char *path;
player = g_new0(struct avrcp_player, 1);
+ player->id = id;
player->sessions = g_slist_prepend(player->sessions, session);
path = device_get_path(session->dev);
@@ -3303,8 +3384,8 @@
media_player_set_name(mp, name);
}
- if (session->controller->player == player && !player->browsed)
- avrcp_set_browsed_player(session, player);
+ if (player->addressed)
+ set_browsed_player(session, player);
return player;
}
@@ -3523,6 +3604,7 @@
return;
}
+ player->addressed = true;
player->uid_counter = get_be16(&pdu->params[3]);
set_ct_player(session, player);
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 4736396..7944b49 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -1102,10 +1102,8 @@
};
static const GDBusPropertyTable media_folder_properties[] = {
- { "Name", "s", get_folder_name, NULL, folder_name_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "NumberOfItems", "u", get_items, NULL, items_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "Name", "s", get_folder_name, NULL, folder_name_exists },
+ { "NumberOfItems", "u", get_items, NULL, items_exists },
{ }
};
@@ -1406,9 +1404,14 @@
"Browsable");
}
+bool media_player_get_browsable(struct media_player *mp)
+{
+ return mp->browsable;
+}
+
void media_player_set_searchable(struct media_player *mp, bool enabled)
{
- if (mp->browsable == enabled)
+ if (mp->searchable == enabled)
return;
DBG("%s", enabled ? "true" : "false");
@@ -1480,12 +1483,15 @@
struct media_item *item = data;
struct media_player *mp = item->player;
struct player_callback *cb = mp->cb;
+ const char *path;
int err;
if (!item->playable || !cb->cbs->play_item)
return btd_error_not_supported(msg);
- err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data);
+ path = mp->search && mp->scope == mp->search ? "/Search" : item->path;
+
+ err = cb->cbs->play_item(mp, path, item->uid, cb->user_data);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 4ad8bfe..54e395a 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -85,6 +85,7 @@
void media_player_set_subtype(struct media_player *mp, const char *subtype);
void media_player_set_name(struct media_player *mp, const char *name);
void media_player_set_browsable(struct media_player *mp, bool enabled);
+bool media_player_get_browsable(struct media_player *mp);
void media_player_set_searchable(struct media_player *mp, bool enabled);
void media_player_set_folder(struct media_player *mp, const char *path,
uint32_t items);
diff --git a/profiles/audioOverBle/btvoice.c b/profiles/audioOverBle/btvoice.c
index fd90b63..d6f192b 100644
--- a/profiles/audioOverBle/btvoice.c
+++ b/profiles/audioOverBle/btvoice.c
@@ -51,11 +51,11 @@
#include "attrib/att.h"
#include "attrib/gatt.h"
#include "src/adapter.h"
-#include "src/attio.h"
#include "src/dbus-common.h"
#include "src/device.h"
#include "src/error.h"
#include "src/log.h"
+#include "src/service.h"
#include "src/shared/util.h"
#include "src/textfile.h"
@@ -70,7 +70,6 @@
bdaddr_t bdaddr;
struct btd_device *device;
GAttrib *attrib;
- guint attioid;
struct gatt_primary *audioOverBle;
struct btvoiceenable voiceenable;
GSList *reports;
@@ -452,20 +451,26 @@
}
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data) {
- struct btvoice_device *btvoicedev = user_data;
+int btvoice_accept(struct btd_service *service) {
+ struct btvoice_device *btvoicedev = btd_service_get_user_data(service);
struct gatt_primary *prim = btvoicedev->audioOverBle;
+ struct btd_device *device = btd_service_get_device(service);
+ GAttrib *attrib = btd_device_get_attrib(device);
btvoicedev->attrib = g_attrib_ref(attrib);
+ btd_service_connecting_complete(service, 0);
+
if (btvoicedev->voiceenable.audioOverBle) {
gatt_discover_char(btvoicedev->attrib, prim->range.start, prim->range.end,
NULL, char_discovered_cb, btvoicedev);
}
+
+ return 0;
}
-static void attio_disconnected_cb(gpointer user_data) {
- struct btvoice_device *btvoicedev = user_data;
+int btvoice_disconnect(struct btd_service *service) {
+ struct btvoice_device *btvoicedev = btd_service_get_user_data(service);
GSList *l;
for (l = btvoicedev->reports; l; l = l->next) {
@@ -476,10 +481,14 @@
g_attrib_unref(btvoicedev->attrib);
btvoicedev->attrib = NULL;
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
}
static struct btvoice_device *register_btvoicedevice(
- struct btd_device *device)
+ struct btd_service *service, struct btd_device *device)
{
struct btvoice_device *btvoicedev;
const bdaddr_t *bdaddr = device_get_address(device);
@@ -493,38 +502,27 @@
btvoicedev->device = btd_device_ref(device);
btvoicedev->bdaddr = *bdaddr;
+ btd_service_set_user_data(service, btvoicedev);
btvoicedevices = g_slist_append(btvoicedevices, btvoicedev);
return btvoicedev;
}
-static void update_btvoicedevice(struct btvoice_device *btvoicedev)
-{
- if (!btvoicedev->voiceenable.audioOverBle) return;
-
- if (btvoicedev->attioid != 0) return;
-
- btvoicedev->attioid =
- btd_device_add_attio_callback(btvoicedev->device, attio_connected_cb,
- attio_disconnected_cb, btvoicedev);
-}
-
-int btvoice_register_audioOverBle(struct btd_device *device,
+int btvoice_register_audioOverBle(struct btd_service *service,
+ struct btd_device *device,
struct btvoiceenable *voiceenable,
struct gatt_primary *prim) {
struct btvoice_device *btvoicedev;
if (!voiceenable->audioOverBle) return 0;
- btvoicedev = register_btvoicedevice(device);
+ btvoicedev = register_btvoicedevice(service, device);
if (btvoicedev == NULL) return -1;
btvoicedev->id = prim->range.start;
btvoicedev->audioOverBle = g_memdup(prim, sizeof(*prim));
btvoicedev->voiceenable.audioOverBle = TRUE;
- update_btvoicedevice(btvoicedev);
-
return 0;
}
@@ -534,11 +532,6 @@
if (btvoicedev->audioOverBle != NULL) return;
- if (btvoicedev->attioid != 0) {
- btd_device_remove_attio_callback(device, btvoicedev->attioid);
- btvoicedev->attioid = 0;
- }
-
if (btvoicedev->attrib != NULL) {
g_attrib_unref(btvoicedev->attrib);
btvoicedev->attrib = NULL;
diff --git a/profiles/audioOverBle/btvoice.h b/profiles/audioOverBle/btvoice.h
index 0cddfe3..6bf57f5 100644
--- a/profiles/audioOverBle/btvoice.h
+++ b/profiles/audioOverBle/btvoice.h
@@ -31,11 +31,15 @@
int l2capChannel;
};
-int btvoice_register_audioOverBle(struct btd_device *device,
+int btvoice_register_audioOverBle(struct btd_service *service,
+ struct btd_device *device,
struct btvoiceenable *voiceenable,
struct gatt_primary *audioOverBle);
void btvoice_unregister_audioOverBle(struct btd_device *device);
+int btvoice_accept(struct btd_service *service);
+int btvoice_disconnect(struct btd_service *service);
+
#endif // BTVOICE_H_
diff --git a/profiles/audioOverBle/manager.c b/profiles/audioOverBle/manager.c
index 7008ac4..b38ed29 100644
--- a/profiles/audioOverBle/manager.c
+++ b/profiles/audioOverBle/manager.c
@@ -67,7 +67,7 @@
if (audioOverBlePrim == NULL)
return -1;
- return btvoice_register_audioOverBle(device, &voiceenable, audioOverBlePrim);
+ return btvoice_register_audioOverBle(service, device, &voiceenable, audioOverBlePrim);
}
@@ -84,6 +84,9 @@
.remote_uuid = AUD_UUID,
.device_probe = btvoice_audioOverBle_probe,
.device_remove = btvoice_audioOverBle_remove,
+ .accept = btvoice_accept,
+ .disconnect = btvoice_disconnect,
+ .auto_connect = true,
};
static void load_config_file(GKeyFile *config)
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
index 2c37492..4ec35fc 100644
--- a/profiles/battery/battery.c
+++ b/profiles/battery/battery.c
@@ -53,7 +53,6 @@
#include "src/service.h"
#include "attrib/att.h"
#include "attrib/gattrib.h"
-#include "src/attio.h"
#include "attrib/gatt.h"
#include "src/shared/util.h"
@@ -63,7 +62,6 @@
#define BATTERY_DIR "/tmp/batteries"
struct service {
- guint attio_id;
uint16_t value_handle;
int value;
};
@@ -72,7 +70,6 @@
uint16_t id;
struct btd_device *device;
GAttrib *attrib;
- guint attioid;
struct gatt_primary *batt_primary;
struct service level;
@@ -225,17 +222,20 @@
}
}
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static int batt_accept(struct btd_service *service)
{
- struct batt_device *battdev = user_data;
+ struct batt_device *battdev = btd_service_get_user_data(service);
struct gatt_primary *prim = battdev->batt_primary;
- GSList *l;
+ struct btd_device *device = btd_service_get_device(service);
+ GAttrib *attrib = btd_device_get_attrib(device);
DBG("BATT connected");
battdev->attrib = g_attrib_ref(attrib);
battdev->level.value = -1;
+ btd_service_connecting_complete(service, 0);
+
if (battdev->level.value_handle == 0) {
DBG("BATT discovering characteristics");
@@ -245,22 +245,24 @@
} else {
check_level(battdev);
}
+
+ return 0;
}
-static void attio_disconnected_cb(gpointer user_data)
+static int batt_disconnect(struct btd_service *service)
{
- struct batt_device *battdev = user_data;
- GSList *l;
+ struct batt_device *battdev = btd_service_get_user_data(service);
DBG("BATT disconnected");
- if (battdev->level.attio_id > 0) {
- g_attrib_unregister(battdev->attrib, battdev->level.attio_id);
- battdev->level.attio_id = 0;
- }
- if (battdev->attrib)
+ if (battdev->attrib) {
g_attrib_unref(battdev->attrib);
- battdev->attrib = NULL;
+ battdev->attrib = NULL;
+ }
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
}
static struct batt_device *batt_register_device(struct btd_device *device,
@@ -276,17 +278,11 @@
battdev->batt_primary = g_memdup(prim, sizeof(*prim));
- battdev->attioid = btd_device_add_attio_callback(device,
- attio_connected_cb,
- attio_disconnected_cb,
- battdev);
-
return battdev;
}
static int batt_unregister_device(struct batt_device *battdev)
{
- btd_device_remove_attio_callback(battdev->device, battdev->attioid);
batt_free_device(battdev);
return 0;
@@ -317,6 +313,7 @@
if (battdev == NULL)
continue;
+ btd_service_set_user_data(service, battdev);
devices = g_slist_append(devices, battdev);
}
@@ -350,6 +347,9 @@
.remote_uuid = BATT_UUID,
.device_probe = batt_probe,
.device_remove = batt_remove,
+ .accept = batt_accept,
+ .disconnect = batt_disconnect,
+ .auto_connect = true,
};
static int batt_init(void)
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
deleted file mode 100644
index e447725..0000000
--- a/profiles/cyclingspeed/cyclingspeed.c
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Tieto Poland
- *
- * 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 <stdbool.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/service.h"
-#include "src/dbus-common.h"
-#include "src/shared/util.h"
-#include "src/error.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "src/attio.h"
-#include "src/log.h"
-
-/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
-#define ATT_HDR_LEN 3
-
-#define ATT_TIMEOUT 30
-
-#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed1"
-#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager1"
-#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher1"
-
-#define WHEEL_REV_SUPPORT 0x01
-#define CRANK_REV_SUPPORT 0x02
-#define MULTI_SENSOR_LOC_SUPPORT 0x04
-
-#define WHEEL_REV_PRESENT 0x01
-#define CRANK_REV_PRESENT 0x02
-
-#define SET_CUMULATIVE_VALUE 0x01
-#define START_SENSOR_CALIBRATION 0x02
-#define UPDATE_SENSOR_LOC 0x03
-#define REQUEST_SUPPORTED_SENSOR_LOC 0x04
-#define RESPONSE_CODE 0x10
-
-#define RSP_SUCCESS 0x01
-#define RSP_NOT_SUPPORTED 0x02
-#define RSP_INVALID_PARAM 0x03
-#define RSP_FAILED 0x04
-
-struct csc;
-
-struct controlpoint_req {
- struct csc *csc;
- uint8_t opcode;
- guint timeout;
- GDBusPendingReply reply_id;
- DBusMessage *msg;
-
- uint8_t pending_location;
-};
-
-struct csc_adapter {
- struct btd_adapter *adapter;
- GSList *devices; /* list of registered devices */
- GSList *watchers;
-};
-
-struct csc {
- struct btd_device *dev;
- struct csc_adapter *cadapter;
-
- GAttrib *attrib;
- guint attioid;
- /* attio id for measurement characteristics value notifications */
- guint attio_measurement_id;
- /* attio id for SC Control Point characteristics value indications */
- guint attio_controlpoint_id;
-
- struct att_range *svc_range;
-
- uint16_t measurement_ccc_handle;
- uint16_t controlpoint_val_handle;
-
- uint16_t feature;
- gboolean has_location;
- uint8_t location;
- uint8_t num_locations;
- uint8_t *locations;
-
- struct controlpoint_req *pending_req;
-};
-
-struct watcher {
- struct csc_adapter *cadapter;
- guint id;
- char *srv;
- char *path;
-};
-
-struct measurement {
- struct csc *csc;
-
- bool has_wheel_rev;
- uint32_t wheel_rev;
- uint16_t last_wheel_time;
-
- bool has_crank_rev;
- uint16_t crank_rev;
- uint16_t last_crank_time;
-};
-
-struct characteristic {
- struct csc *csc;
- char uuid[MAX_LEN_UUID_STR + 1];
-};
-
-static GSList *csc_adapters = NULL;
-
-static const char * const location_enum[] = {
- "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
- "right-crank", "left-pedal", "right-pedal", "front-hub",
- "rear-dropout", "chainstay", "rear-wheel", "rear-hub"
-};
-
-static const char *location2str(uint8_t value)
-{
- if (value < G_N_ELEMENTS(location_enum))
- return location_enum[value];
-
- info("Body Sensor Location [%d] is RFU", value);
-
- return location_enum[0];
-}
-
-static int str2location(const char *location)
-{
- size_t i;
-
- for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
- if (!strcmp(location_enum[i], location))
- return i;
-
- return -1;
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
- const struct csc_adapter *cadapter = a;
- const struct btd_adapter *adapter = b;
-
- if (adapter == cadapter->adapter)
- return 0;
-
- return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct csc *csc = a;
- const struct btd_device *dev = b;
-
- if (dev == csc->dev)
- return 0;
-
- return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
- const struct watcher *watcher = a;
- const struct watcher *match = b;
- int ret;
-
- ret = g_strcmp0(watcher->srv, match->srv);
- if (ret != 0)
- return ret;
-
- return g_strcmp0(watcher->path, match->path);
-}
-
-static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
-{
- GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
-
- if (!l)
- return NULL;
-
- return l->data;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_free(watcher->path);
- g_free(watcher->srv);
- g_free(watcher);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
- const char *path)
-{
- struct watcher *match;
- GSList *l;
-
- match = g_new0(struct watcher, 1);
- match->srv = g_strdup(sender);
- match->path = g_strdup(path);
-
- l = g_slist_find_custom(list, match, cmp_watcher);
- destroy_watcher(match);
-
- if (l != NULL)
- return l->data;
-
- return NULL;
-}
-
-static void remove_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_csc_adapter(gpointer user_data)
-{
- struct csc_adapter *cadapter = user_data;
-
- g_slist_free_full(cadapter->watchers, remove_watcher);
-
- g_free(cadapter);
-}
-
-static void destroy_csc(gpointer user_data)
-{
- struct csc *csc = user_data;
-
- if (csc->attioid > 0)
- btd_device_remove_attio_callback(csc->dev, csc->attioid);
-
- if (csc->attrib != NULL) {
- g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
- g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
- g_attrib_unref(csc->attrib);
- }
-
- btd_device_unref(csc->dev);
- g_free(csc->svc_range);
- g_free(csc->locations);
- g_free(csc);
-}
-
-static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- char *msg = user_data;
-
- if (status != 0)
- error("%s failed", msg);
-
- g_free(msg);
-}
-
-static gboolean controlpoint_timeout(gpointer user_data)
-{
- struct controlpoint_req *req = user_data;
-
- if (req->opcode == UPDATE_SENSOR_LOC) {
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".Failed",
- "Operation failed (timeout)");
- } else if (req->opcode == SET_CUMULATIVE_VALUE) {
- DBusMessage *reply;
-
- reply = btd_error_failed(req->msg,
- "Operation failed (timeout)");
-
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-
- dbus_message_unref(req->msg);
- }
-
- req->csc->pending_req = NULL;
- g_free(req);
-
- return FALSE;
-}
-
-static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct controlpoint_req *req = user_data;
-
- if (status == 0) {
- req->timeout = g_timeout_add_seconds(ATT_TIMEOUT,
- controlpoint_timeout,
- req);
- return;
- }
-
- error("SC Control Point write failed (opcode=%d)", req->opcode);
-
- if (req->opcode == UPDATE_SENSOR_LOC) {
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".Failed",
- "Operation failed (%d)", status);
- } else if (req->opcode == SET_CUMULATIVE_VALUE) {
- DBusMessage *reply;
-
- reply = btd_error_failed(req->msg, "Operation failed");
-
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-
- dbus_message_unref(req->msg);
- }
-
- req->csc->pending_req = NULL;
- g_free(req);
-}
-
-static void read_supported_locations(struct csc *csc)
-{
- struct controlpoint_req *req;
-
- req = g_new0(struct controlpoint_req, 1);
- req->csc = csc;
- req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
-
- csc->pending_req = req;
-
- gatt_write_char(csc->attrib, csc->controlpoint_val_handle,
- &req->opcode, sizeof(req->opcode),
- controlpoint_write_cb, req);
-}
-
-static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct csc *csc = user_data;
- uint8_t value[2];
- ssize_t vlen;
-
- if (status) {
- error("CSC Feature read failed: %s", att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, value, sizeof(value));
- if (vlen < 0) {
- error("Protocol error");
- return;
- }
-
- if (vlen != sizeof(value)) {
- error("Invalid value length for CSC Feature");
- return;
- }
-
- csc->feature = get_le16(value);
-
- if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
- && (csc->locations == NULL))
- read_supported_locations(csc);
-}
-
-static void read_location_cb(guint8 status, const guint8 *pdu,
- guint16 len, gpointer user_data)
-{
- struct csc *csc = user_data;
- uint8_t value;
- ssize_t vlen;
-
- if (status) {
- error("Sensor Location read failed: %s", att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, &value, sizeof(value));
- if (vlen < 0) {
- error("Protocol error");
- return;
- }
-
- if (vlen != sizeof(value)) {
- error("Invalid value length for Sensor Location");
- return;
- }
-
- csc->has_location = TRUE;
- csc->location = value;
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- device_get_path(csc->dev),
- CYCLINGSPEED_INTERFACE, "Location");
-}
-
-static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
-{
- struct characteristic *ch = user_data;
- struct gatt_desc *desc;
- uint8_t attr_val[2];
- char *msg = NULL;
-
- if (status != 0) {
- error("Discover %s descriptors failed: %s", ch->uuid,
- att_ecode2str(status));
- goto done;
- }
-
- /* There will be only one descriptor on list and it will be CCC */
- desc = descs->data;
-
- if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
- ch->csc->measurement_ccc_handle = desc->handle;
-
- if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
- put_le16(0x0000, attr_val);
- msg = g_strdup("Disable measurement");
- } else {
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
- attr_val);
- msg = g_strdup("Enable measurement");
- }
- } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
- put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
- msg = g_strdup("Enable SC Control Point indications");
- } else {
- goto done;
- }
-
- gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
- sizeof(attr_val), char_write_cb, msg);
-
-done:
- g_free(ch);
-}
-
-static void discover_desc(struct csc *csc, struct gatt_char *c,
- struct gatt_char *c_next)
-{
- struct characteristic *ch;
- uint16_t start, end;
- bt_uuid_t uuid;
-
- start = c->value_handle + 1;
-
- if (c_next != NULL) {
- if (start == c_next->handle)
- return;
- end = c_next->handle - 1;
- } else if (c->value_handle != csc->svc_range->end) {
- end = csc->svc_range->end;
- } else {
- return;
- }
-
- ch = g_new0(struct characteristic, 1);
- ch->csc = csc;
- memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
-
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
- gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
- ch);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
- struct watcher *w = data;
- struct measurement *m = user_data;
- struct csc *csc = m->csc;
- const char *path = device_get_path(csc->dev);
- DBusMessageIter iter;
- DBusMessageIter dict;
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(w->srv, w->path,
- CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
- if (msg == NULL)
- return;
-
- dbus_message_iter_init_append(msg, &iter);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
- 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 (m->has_wheel_rev) {
- dict_append_entry(&dict, "WheelRevolutions",
- DBUS_TYPE_UINT32, &m->wheel_rev);
- dict_append_entry(&dict, "LastWheelEventTime",
- DBUS_TYPE_UINT16, &m->last_wheel_time);
- }
-
- if (m->has_crank_rev) {
- dict_append_entry(&dict, "CrankRevolutions",
- DBUS_TYPE_UINT16, &m->crank_rev);
- dict_append_entry(&dict, "LastCrankEventTime",
- DBUS_TYPE_UINT16, &m->last_crank_time);
- }
-
- dbus_message_iter_close_container(&iter, &dict);
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void process_measurement(struct csc *csc, const uint8_t *pdu,
- uint16_t len)
-{
- struct measurement m;
- uint8_t flags;
-
- flags = *pdu;
-
- pdu++;
- len--;
-
- memset(&m, 0, sizeof(m));
-
- if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
- if (len < 6) {
- error("Wheel revolutions data fields missing");
- return;
- }
-
- m.has_wheel_rev = true;
- m.wheel_rev = get_le32(pdu);
- m.last_wheel_time = get_le16(pdu + 4);
- pdu += 6;
- len -= 6;
- }
-
- if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
- if (len < 4) {
- error("Crank revolutions data fields missing");
- return;
- }
-
- m.has_crank_rev = true;
- m.crank_rev = get_le16(pdu);
- m.last_crank_time = get_le16(pdu + 2);
- pdu += 4;
- len -= 4;
- }
-
- /* Notify all registered watchers */
- m.csc = csc;
- g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
-}
-
-static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct csc *csc = user_data;
-
- /* should be at least opcode (1b) + handle (2b) */
- if (len < 3) {
- error("Invalid PDU received");
- return;
- }
-
- process_measurement(csc, pdu + 3, len - 3);
-}
-
-static void controlpoint_property_reply(struct controlpoint_req *req,
- uint8_t code)
-{
- switch (code) {
- case RSP_SUCCESS:
- g_dbus_pending_property_success(req->reply_id);
- break;
-
- case RSP_NOT_SUPPORTED:
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".NotSupported",
- "Feature is not supported");
- break;
-
- case RSP_INVALID_PARAM:
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- break;
-
- case RSP_FAILED:
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".Failed",
- "Operation failed");
- break;
-
- default:
- g_dbus_pending_property_error(req->reply_id,
- ERROR_INTERFACE ".Failed",
- "Operation failed (%d)", code);
- break;
- }
-}
-
-static void controlpoint_method_reply(struct controlpoint_req *req,
- uint8_t code)
-{
- DBusMessage *reply;
-
- switch (code) {
- case RSP_SUCCESS:
- reply = dbus_message_new_method_return(req->msg);
- break;
- case RSP_NOT_SUPPORTED:
- reply = btd_error_not_supported(req->msg);
- break;
- case RSP_INVALID_PARAM:
- reply = btd_error_invalid_args(req->msg);
- break;
- case RSP_FAILED:
- reply = btd_error_failed(req->msg, "Failed");
- break;
- default:
- reply = btd_error_failed(req->msg, "Unknown error");
- break;
- }
-
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-
- dbus_message_unref(req->msg);
-}
-
-static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct csc *csc = user_data;
- struct controlpoint_req *req = csc->pending_req;
- uint8_t opcode;
- uint8_t req_opcode;
- uint8_t rsp_code;
- uint8_t *opdu;
- uint16_t olen;
- size_t plen;
-
- if (len < ATT_HDR_LEN) {
- error("Invalid PDU received");
- return;
- }
-
- /* skip ATT header */
- pdu += ATT_HDR_LEN;
- len -= ATT_HDR_LEN;
-
- if (len < 1) {
- error("Op Code missing");
- goto done;
- }
-
- opcode = *pdu;
- pdu++;
- len--;
-
- if (opcode != RESPONSE_CODE) {
- DBG("Unsupported Op Code received (%d)", opcode);
- goto done;
- }
-
- if (len < 2) {
- error("Invalid Response Code PDU received");
- goto done;
- }
-
- req_opcode = *pdu;
- rsp_code = *(pdu + 1);
- pdu += 2;
- len -= 2;
-
- if (req == NULL || req->opcode != req_opcode) {
- DBG("Indication received without pending request");
- goto done;
- }
-
- switch (req->opcode) {
- case SET_CUMULATIVE_VALUE:
- controlpoint_method_reply(req, rsp_code);
- break;
-
- case REQUEST_SUPPORTED_SENSOR_LOC:
- if (rsp_code == RSP_SUCCESS) {
- csc->num_locations = len;
- csc->locations = g_memdup(pdu, len);
- } else {
- error("Failed to read Supported Sendor Locations");
- }
- break;
-
- case UPDATE_SENSOR_LOC:
- csc->location = req->pending_location;
-
- controlpoint_property_reply(req, rsp_code);
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- device_get_path(csc->dev),
- CYCLINGSPEED_INTERFACE, "Location");
- break;
- }
-
- csc->pending_req = NULL;
- g_source_remove(req->timeout);
- g_free(req);
-
-done:
- opdu = g_attrib_get_buffer(csc->attrib, &plen);
- olen = enc_confirmation(opdu, plen);
- if (olen > 0)
- g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
-{
- struct csc *csc = user_data;
- uint16_t feature_val_handle = 0;
-
- if (status) {
- error("Discover CSCS characteristics: %s",
- att_ecode2str(status));
- return;
- }
-
- for (; chars; chars = chars->next) {
- struct gatt_char *c = chars->data;
- struct gatt_char *c_next =
- (chars->next ? chars->next->data : NULL);
-
- if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
- csc->attio_measurement_id =
- g_attrib_register(csc->attrib,
- ATT_OP_HANDLE_NOTIFY, c->value_handle,
- measurement_notify_handler, csc, NULL);
-
- discover_desc(csc, c, c_next);
- } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
- feature_val_handle = c->value_handle;
- } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
- DBG("Sensor Location supported");
- gatt_read_char(csc->attrib, c->value_handle,
- read_location_cb, csc);
- } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
- DBG("SC Control Point supported");
- csc->controlpoint_val_handle = c->value_handle;
-
- csc->attio_controlpoint_id = g_attrib_register(
- csc->attrib, ATT_OP_HANDLE_IND,
- c->value_handle,
- controlpoint_ind_handler, csc, NULL);
-
- discover_desc(csc, c, c_next);
- }
- }
-
- if (feature_val_handle > 0)
- gatt_read_char(csc->attrib, feature_val_handle,
- read_feature_cb, csc);
-}
-
-static void enable_measurement(gpointer data, gpointer user_data)
-{
- struct csc *csc = data;
- uint16_t handle = csc->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (csc->attrib == NULL || !handle)
- return;
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
- msg = g_strdup("Enable measurement");
-
- gatt_write_char(csc->attrib, handle, value, sizeof(value),
- char_write_cb, msg);
-}
-
-static void disable_measurement(gpointer data, gpointer user_data)
-{
- struct csc *csc = data;
- uint16_t handle = csc->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (csc->attrib == NULL || !handle)
- return;
-
- put_le16(0x0000, value);
- msg = g_strdup("Disable measurement");
-
- gatt_write_char(csc->attrib, handle, value, sizeof(value),
- char_write_cb, msg);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct csc *csc = user_data;
-
- DBG("");
-
- csc->attrib = g_attrib_ref(attrib);
-
- gatt_discover_char(csc->attrib, csc->svc_range->start,
- csc->svc_range->end, NULL,
- discover_char_cb, csc);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
- struct csc *csc = user_data;
-
- DBG("");
-
- if (csc->attio_measurement_id > 0) {
- g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
- csc->attio_measurement_id = 0;
- }
-
- if (csc->attio_controlpoint_id > 0) {
- g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
- csc->attio_controlpoint_id = 0;
- }
-
- g_attrib_unref(csc->attrib);
- csc->attrib = NULL;
-}
-
-static void watcher_exit_cb(DBusConnection *conn, void *user_data)
-{
- struct watcher *watcher = user_data;
- struct csc_adapter *cadapter = watcher->cadapter;
-
- DBG("cycling watcher [%s] disconnected", watcher->path);
-
- cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
- g_dbus_remove_watch(conn, watcher->id);
-
- if (g_slist_length(cadapter->watchers) == 0)
- g_slist_foreach(cadapter->devices, disable_measurement, 0);
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct csc_adapter *cadapter = data;
- struct watcher *watcher;
- const char *sender = dbus_message_get_sender(msg);
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(cadapter->watchers, sender, path);
- if (watcher != NULL)
- return btd_error_already_exists(msg);
-
- watcher = g_new0(struct watcher, 1);
- watcher->cadapter = cadapter;
- watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
- watcher, destroy_watcher);
- watcher->srv = g_strdup(sender);
- watcher->path = g_strdup(path);
-
- if (g_slist_length(cadapter->watchers) == 0)
- g_slist_foreach(cadapter->devices, enable_measurement, 0);
-
- cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
-
- DBG("cycling watcher [%s] registered", path);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct csc_adapter *cadapter = data;
- struct watcher *watcher;
- const char *sender = dbus_message_get_sender(msg);
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(cadapter->watchers, sender, path);
- if (watcher == NULL)
- return btd_error_does_not_exist(msg);
-
- cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
- g_dbus_remove_watch(conn, watcher->id);
-
- if (g_slist_length(cadapter->watchers) == 0)
- g_slist_foreach(cadapter->devices, disable_measurement, 0);
-
- DBG("cycling watcher [%s] unregistered", path);
-
- return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable cyclingspeed_manager_methods[] = {
- { GDBUS_METHOD("RegisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- register_watcher) },
- { GDBUS_METHOD("UnregisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- unregister_watcher) },
- { }
-};
-
-static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
-{
- struct csc_adapter *cadapter;
-
- cadapter = g_new0(struct csc_adapter, 1);
- cadapter->adapter = adapter;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- adapter_get_path(adapter),
- CYCLINGSPEED_MANAGER_INTERFACE,
- cyclingspeed_manager_methods,
- NULL, NULL, cadapter,
- destroy_csc_adapter)) {
- error("D-Bus failed to register %s interface",
- CYCLINGSPEED_MANAGER_INTERFACE);
- destroy_csc_adapter(cadapter);
- return -EIO;
- }
-
- csc_adapters = g_slist_prepend(csc_adapters, cadapter);
-
- return 0;
-}
-
-static void csc_adapter_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- struct csc_adapter *cadapter;
-
- cadapter = find_csc_adapter(adapter);
- if (cadapter == NULL)
- return;
-
- csc_adapters = g_slist_remove(csc_adapters, cadapter);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- adapter_get_path(cadapter->adapter),
- CYCLINGSPEED_MANAGER_INTERFACE);
-}
-
-static gboolean property_get_location(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct csc *csc = data;
- const char *loc;
-
- if (!csc->has_location)
- return FALSE;
-
- loc = location2str(csc->location);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
-
- return TRUE;
-}
-
-static void property_set_location(const GDBusPropertyTable *property,
- DBusMessageIter *iter,
- GDBusPendingPropertySet id, void *data)
-{
- struct csc *csc = data;
- char *loc;
- int loc_val;
- uint8_t att_val[2];
- struct controlpoint_req *req;
-
- if (csc->pending_req != NULL) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InProgress",
- "Operation already in progress");
- return;
- }
-
- if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".NotSupported",
- "Feature is not supported");
- return;
- }
-
- 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, &loc);
-
- loc_val = str2location(loc);
-
- if (loc_val < 0) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- req = g_new(struct controlpoint_req, 1);
- req->csc = csc;
- req->reply_id = id;
- req->opcode = UPDATE_SENSOR_LOC;
- req->pending_location = loc_val;
-
- csc->pending_req = req;
-
- att_val[0] = UPDATE_SENSOR_LOC;
- att_val[1] = loc_val;
-
- gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
- sizeof(att_val), controlpoint_write_cb, req);
-}
-
-static gboolean property_exists_location(const GDBusPropertyTable *property,
- void *data)
-{
- struct csc *csc = data;
-
- return csc->has_location;
-}
-
-static gboolean property_get_locations(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct csc *csc = data;
- DBusMessageIter entry;
- int i;
-
- if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
- return FALSE;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING, &entry);
- for (i = 0; i < csc->num_locations; i++) {
- char *loc = g_strdup(location2str(csc->locations[i]));
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
- g_free(loc);
- }
-
- dbus_message_iter_close_container(iter, &entry);
-
- return TRUE;
-}
-
-static gboolean property_exists_locations(const GDBusPropertyTable *property,
- void *data)
-{
- struct csc *csc = data;
-
- return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
-}
-
-static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct csc *csc = data;
- dbus_bool_t val;
-
- val = !!(csc->feature & WHEEL_REV_SUPPORT);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
- return TRUE;
-}
-
-static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct csc *csc = data;
- dbus_bool_t val;
-
- val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
- return TRUE;
-}
-
-static const GDBusPropertyTable cyclingspeed_device_properties[] = {
- { "Location", "s", property_get_location, property_set_location,
- property_exists_location },
- { "SupportedLocations", "as", property_get_locations, NULL,
- property_exists_locations },
- { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
- { "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
- { }
-};
-
-static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct csc *csc = data;
- dbus_uint32_t value;
- struct controlpoint_req *req;
- uint8_t att_val[5]; /* uint8 opcode + uint32 value */
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- if (csc->pending_req != NULL)
- return btd_error_in_progress(msg);
-
- req = g_new(struct controlpoint_req, 1);
- req->csc = csc;
- req->opcode = SET_CUMULATIVE_VALUE;
- req->msg = dbus_message_ref(msg);
-
- csc->pending_req = req;
-
- att_val[0] = SET_CUMULATIVE_VALUE;
- put_le32(value, att_val + 1);
-
- gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
- sizeof(att_val), controlpoint_write_cb, req);
-
- return NULL;
-}
-
-static const GDBusMethodTable cyclingspeed_device_methods[] = {
- { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
- GDBUS_ARGS({ "value", "u" }), NULL,
- set_cumulative_wheel_rev) },
- { }
-};
-
-static int csc_device_probe(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct btd_adapter *adapter;
- struct csc_adapter *cadapter;
- struct csc *csc;
- struct gatt_primary *prim;
-
- prim = btd_device_get_primary(device, CYCLING_SC_UUID);
- if (prim == NULL)
- return -EINVAL;
-
- adapter = device_get_adapter(device);
-
- cadapter = find_csc_adapter(adapter);
- if (cadapter == NULL)
- return -1;
-
- csc = g_new0(struct csc, 1);
- csc->dev = btd_device_ref(device);
- csc->cadapter = cadapter;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- device_get_path(device),
- CYCLINGSPEED_INTERFACE,
- cyclingspeed_device_methods,
- NULL,
- cyclingspeed_device_properties,
- csc, destroy_csc)) {
- error("D-Bus failed to register %s interface",
- CYCLINGSPEED_INTERFACE);
- destroy_csc(csc);
- return -EIO;
- }
-
- csc->svc_range = g_new0(struct att_range, 1);
- csc->svc_range->start = prim->range.start;
- csc->svc_range->end = prim->range.end;
-
- cadapter->devices = g_slist_prepend(cadapter->devices, csc);
-
- csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
- attio_disconnected_cb, csc);
-
- return 0;
-}
-
-static void csc_device_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct btd_adapter *adapter;
- struct csc_adapter *cadapter;
- struct csc *csc;
- GSList *l;
-
- adapter = device_get_adapter(device);
-
- cadapter = find_csc_adapter(adapter);
- if (cadapter == NULL)
- return;
-
- l = g_slist_find_custom(cadapter->devices, device, cmp_device);
- if (l == NULL)
- return;
-
- csc = l->data;
-
- cadapter->devices = g_slist_remove(cadapter->devices, csc);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(device),
- CYCLINGSPEED_INTERFACE);
-}
-
-static struct btd_profile cscp_profile = {
- .name = "Cycling Speed and Cadence GATT Driver",
- .remote_uuid = CYCLING_SC_UUID,
-
- .adapter_probe = csc_adapter_probe,
- .adapter_remove = csc_adapter_remove,
-
- .device_probe = csc_device_probe,
- .device_remove = csc_device_remove,
-};
-
-static int cyclingspeed_init(void)
-{
- return btd_profile_register(&cscp_profile);
-}
-
-static void cyclingspeed_exit(void)
-{
- btd_profile_unregister(&cscp_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- cyclingspeed_init, cyclingspeed_exit)
diff --git a/profiles/deviceinfo/deviceinfo.c b/profiles/deviceinfo/deviceinfo.c
index 0c48f00..fa94efe 100644
--- a/profiles/deviceinfo/deviceinfo.c
+++ b/profiles/deviceinfo/deviceinfo.c
@@ -111,17 +111,17 @@
gatt_db_service_foreach_char(attr, handle_characteristic, device);
}
-static int deviceinfo_driver_probe(struct btd_service *service)
+static int deviceinfo_probe(struct btd_service *service)
{
return 0;
}
-static void deviceinfo_driver_remove(struct btd_service *service)
+static void deviceinfo_remove(struct btd_service *service)
{
}
-static int deviceinfo_driver_accept(struct btd_service *service)
+static int deviceinfo_accept(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct gatt_db *db = btd_device_get_gatt_db(device);
@@ -136,6 +136,15 @@
gatt_db_foreach_service(db, &deviceinfo_uuid,
foreach_deviceinfo_service, device);
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int deviceinfo_disconnect(struct btd_service *service)
+{
+ btd_service_disconnecting_complete(service, 0);
+
return 0;
}
@@ -143,9 +152,10 @@
.name = "deviceinfo",
.remote_uuid = DEVICE_INFORMATION_UUID,
.external = true,
- .device_probe = deviceinfo_driver_probe,
- .device_remove = deviceinfo_driver_remove,
- .accept = deviceinfo_driver_accept,
+ .device_probe = deviceinfo_probe,
+ .device_remove = deviceinfo_remove,
+ .accept = deviceinfo_accept,
+ .disconnect = deviceinfo_disconnect,
};
static int deviceinfo_init(void)
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 35b996c..47c8c25 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -57,8 +57,6 @@
struct gatt_db_attribute *attr;
};
-static GSList *devices;
-
static void gas_free(struct gas *gas)
{
gatt_db_unref(gas->db);
@@ -67,14 +65,6 @@
g_free(gas);
}
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct gas *gas = a;
- const struct btd_device *device = b;
-
- return gas->device == device ? 0 : -1;
-}
-
static char *name2utf8(const uint8_t *name, uint16_t len)
{
char utf8_name[HCI_MAX_NAME_LENGTH + 2];
@@ -205,19 +195,17 @@
gatt_db_service_foreach_char(gas->attr, handle_characteristic, gas);
}
-static int gap_driver_probe(struct btd_service *service)
+static int gap_probe(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
- struct gas *gas;
- GSList *l;
+ struct gas *gas = btd_service_get_user_data(service);
char addr[18];
ba2str(device_get_address(device), addr);
DBG("GAP profile probe (%s)", addr);
/* Ignore, if we were probed for this device already */
- l = g_slist_find_custom(devices, device, cmp_device);
- if (l) {
+ if (gas) {
error("Profile probed twice for the same device!");
return -1;
}
@@ -227,30 +215,26 @@
return -1;
gas->device = btd_device_ref(device);
- devices = g_slist_append(devices, gas);
+ btd_service_set_user_data(service, gas);
return 0;
}
-static void gap_driver_remove(struct btd_service *service)
+static void gap_remove(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct gas *gas;
- GSList *l;
char addr[18];
ba2str(device_get_address(device), addr);
DBG("GAP profile remove (%s)", addr);
- l = g_slist_find_custom(devices, device, cmp_device);
- if (!l) {
+ gas = btd_service_get_user_data(service);
+ if (!gas) {
error("GAP service not handled by profile");
return;
}
- gas = l->data;
-
- devices = g_slist_remove(devices, gas);
gas_free(gas);
}
@@ -267,32 +251,32 @@
handle_gap_service(gas);
}
-static int gap_driver_accept(struct btd_service *service)
+static void gas_reset(struct gas *gas)
+{
+ gas->attr = NULL;
+ gatt_db_unref(gas->db);
+ gas->db = NULL;
+ bt_gatt_client_unref(gas->client);
+ gas->client = NULL;
+}
+
+static int gap_accept(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct gatt_db *db = btd_device_get_gatt_db(device);
struct bt_gatt_client *client = btd_device_get_gatt_client(device);
- struct gas *gas;
- GSList *l;
+ struct gas *gas = btd_service_get_user_data(service);
char addr[18];
bt_uuid_t gap_uuid;
ba2str(device_get_address(device), addr);
DBG("GAP profile accept (%s)", addr);
- l = g_slist_find_custom(devices, device, cmp_device);
- if (!l) {
+ if (!gas) {
error("GAP service not handled by profile");
return -1;
}
- gas = l->data;
-
- /* Clean-up any old client/db and acquire the new ones */
- gas->attr = NULL;
- gatt_db_unref(gas->db);
- bt_gatt_client_unref(gas->client);
-
gas->db = gatt_db_ref(db);
gas->client = bt_gatt_client_ref(client);
@@ -300,24 +284,40 @@
bt_uuid16_create(&gap_uuid, GAP_UUID16);
gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas);
+ if (!gas->attr) {
+ error("GAP attribute not found");
+ gas_reset(gas);
+ return -1;
+ }
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int gap_disconnect(struct btd_service *service)
+{
+ struct gas *gas = btd_service_get_user_data(service);
+
+ gas_reset(gas);
+
+ btd_service_disconnecting_complete(service, 0);
+
return 0;
}
static struct btd_profile gap_profile = {
.name = "gap-profile",
.remote_uuid = GAP_UUID,
- .device_probe = gap_driver_probe,
- .device_remove = gap_driver_remove,
- .accept = gap_driver_accept
+ .device_probe = gap_probe,
+ .device_remove = gap_remove,
+ .accept = gap_accept,
+ .disconnect = gap_disconnect,
};
static int gap_init(void)
{
- devices = NULL;
-
- btd_profile_register(&gap_profile);
-
- return 0;
+ return btd_profile_register(&gap_profile);
}
static void gap_exit(void)
diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
deleted file mode 100644
index 9e8c499..0000000
--- a/profiles/heartrate/heartrate.c
+++ /dev/null
@@ -1,870 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2012 Tieto Poland
- *
- * 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 <stdbool.h>
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/adapter.h"
-#include "src/dbus-common.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/shared/util.h"
-#include "src/service.h"
-#include "src/error.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "src/attio.h"
-#include "src/log.h"
-
-#define HEART_RATE_INTERFACE "org.bluez.HeartRate1"
-#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager1"
-#define HEART_RATE_WATCHER_INTERFACE "org.bluez.HeartRateWatcher1"
-
-#define HR_VALUE_FORMAT 0x01
-#define SENSOR_CONTACT_DETECTED 0x02
-#define SENSOR_CONTACT_SUPPORT 0x04
-#define ENERGY_EXP_STATUS 0x08
-#define RR_INTERVAL 0x10
-
-struct heartrate_adapter {
- struct btd_adapter *adapter;
- GSList *devices;
- GSList *watchers;
-};
-
-struct heartrate {
- struct btd_device *dev;
- struct heartrate_adapter *hradapter;
- GAttrib *attrib;
- guint attioid;
- guint attionotid;
-
- struct att_range *svc_range; /* primary svc range */
-
- uint16_t measurement_ccc_handle;
- uint16_t hrcp_val_handle;
-
- gboolean has_location;
- uint8_t location;
-};
-
-struct watcher {
- struct heartrate_adapter *hradapter;
- guint id;
- char *srv;
- char *path;
-};
-
-struct measurement {
- struct heartrate *hr;
- uint16_t value;
- gboolean has_energy;
- uint16_t energy;
- gboolean has_contact;
- gboolean contact;
- uint16_t num_interval;
- uint16_t *interval;
-};
-
-static GSList *heartrate_adapters = NULL;
-
-static const char * const location_enum[] = {
- "other",
- "chest",
- "wrist",
- "finger",
- "hand",
- "earlobe",
- "foot",
-};
-
-static const char *location2str(uint8_t value)
-{
- if (value < G_N_ELEMENTS(location_enum))
- return location_enum[value];
-
- error("Body Sensor Location [%d] is RFU", value);
-
- return NULL;
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
- const struct heartrate_adapter *hradapter = a;
- const struct btd_adapter *adapter = b;
-
- if (adapter == hradapter->adapter)
- return 0;
-
- return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct heartrate *hr = a;
- const struct btd_device *dev = b;
-
- if (dev == hr->dev)
- return 0;
-
- return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
- const struct watcher *watcher = a;
- const struct watcher *match = b;
- int ret;
-
- ret = g_strcmp0(watcher->srv, match->srv);
- if (ret != 0)
- return ret;
-
- return g_strcmp0(watcher->path, match->path);
-}
-
-static struct heartrate_adapter *
-find_heartrate_adapter(struct btd_adapter *adapter)
-{
- GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
- cmp_adapter);
- if (!l)
- return NULL;
-
- return l->data;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_free(watcher->path);
- g_free(watcher->srv);
- g_free(watcher);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
- const char *path)
-{
- struct watcher *match;
- GSList *l;
-
- match = g_new0(struct watcher, 1);
- match->srv = g_strdup(sender);
- match->path = g_strdup(path);
-
- l = g_slist_find_custom(list, match, cmp_watcher);
- destroy_watcher(match);
-
- if (l != NULL)
- return l->data;
-
- return NULL;
-}
-
-static void destroy_heartrate(gpointer user_data)
-{
- struct heartrate *hr = user_data;
-
- if (hr->attioid > 0)
- btd_device_remove_attio_callback(hr->dev, hr->attioid);
-
- if (hr->attrib != NULL) {
- g_attrib_unregister(hr->attrib, hr->attionotid);
- g_attrib_unref(hr->attrib);
- }
-
- btd_device_unref(hr->dev);
- g_free(hr->svc_range);
- g_free(hr);
-}
-
-static void remove_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_heartrate_adapter(gpointer user_data)
-{
- struct heartrate_adapter *hradapter = user_data;
-
- g_slist_free_full(hradapter->watchers, remove_watcher);
-
- g_free(hradapter);
-}
-
-static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
- guint16 len, gpointer user_data)
-{
- struct heartrate *hr = user_data;
- uint8_t value;
- ssize_t vlen;
-
- if (status != 0) {
- error("Body Sensor Location read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, &value, sizeof(value));
- if (vlen < 0) {
- error("Protocol error");
- return;
- }
-
- if (vlen != sizeof(value)) {
- error("Invalid length for Body Sensor Location");
- return;
- }
-
- hr->has_location = TRUE;
- hr->location = value;
-}
-
-static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- char *msg = user_data;
-
- if (status != 0)
- error("%s failed", msg);
-
- g_free(msg);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
- struct watcher *w = data;
- struct measurement *m = user_data;
- struct heartrate *hr = m->hr;
- const char *path = device_get_path(hr->dev);
- DBusMessageIter iter;
- DBusMessageIter dict;
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(w->srv, w->path,
- HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
- if (msg == NULL)
- return;
-
- dbus_message_iter_init_append(msg, &iter);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
- 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);
-
- dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value);
-
- if (m->has_energy)
- dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
- &m->energy);
-
- if (m->has_contact)
- dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
- &m->contact);
-
- if (m->num_interval > 0)
- dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
- &m->interval, m->num_interval);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
- uint16_t len)
-{
- struct measurement m;
- uint8_t flags;
-
- flags = *pdu;
-
- pdu++;
- len--;
-
- memset(&m, 0, sizeof(m));
-
- if (flags & HR_VALUE_FORMAT) {
- if (len < 2) {
- error("Heart Rate Measurement field missing");
- return;
- }
-
- m.value = get_le16(pdu);
- pdu += 2;
- len -= 2;
- } else {
- if (len < 1) {
- error("Heart Rate Measurement field missing");
- return;
- }
-
- m.value = *pdu;
- pdu++;
- len--;
- }
-
- if (flags & ENERGY_EXP_STATUS) {
- if (len < 2) {
- error("Energy Expended field missing");
- return;
- }
-
- m.has_energy = TRUE;
- m.energy = get_le16(pdu);
- pdu += 2;
- len -= 2;
- }
-
- if (flags & RR_INTERVAL) {
- int i;
-
- if (len == 0 || (len % 2 != 0)) {
- error("RR-Interval field malformed");
- return;
- }
-
- m.num_interval = len / 2;
- m.interval = g_new(uint16_t, m.num_interval);
-
- for (i = 0; i < m.num_interval; pdu += 2, i++)
- m.interval[i] = get_le16(pdu);
- }
-
- if (flags & SENSOR_CONTACT_SUPPORT) {
- m.has_contact = TRUE;
- m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
- }
-
- /* Notify all registered watchers */
- m.hr = hr;
- g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
-
- g_free(m.interval);
-}
-
-static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
-{
- struct heartrate *hr = user_data;
-
- /* should be at least opcode (1b) + handle (2b) */
- if (len < 3) {
- error("Invalid PDU received");
- return;
- }
-
- process_measurement(hr, pdu + 3, len - 3);
-}
-
-static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
-{
- struct heartrate *hr = user_data;
- struct gatt_desc *desc;
- uint8_t attr_val[2];
- char *msg;
-
- if (status != 0) {
- error("Discover Heart Rate Measurement descriptors failed: %s",
- att_ecode2str(status));
- return;
- }
-
- /* There will be only one descriptor on list and it will be CCC */
- desc = descs->data;
-
- hr->measurement_ccc_handle = desc->handle;
-
- if (g_slist_length(hr->hradapter->watchers) == 0) {
- put_le16(0x0000, attr_val);
- msg = g_strdup("Disable measurement");
- } else {
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
- msg = g_strdup("Enable measurement");
- }
-
- gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
- char_write_cb, msg);
-}
-
-static void discover_measurement_ccc(struct heartrate *hr,
- struct gatt_char *c, struct gatt_char *c_next)
-{
- uint16_t start, end;
- bt_uuid_t uuid;
-
- start = c->value_handle + 1;
-
- if (c_next != NULL) {
- if (start == c_next->handle)
- return;
- end = c_next->handle - 1;
- } else if (c->value_handle != hr->svc_range->end) {
- end = hr->svc_range->end;
- } else {
- return;
- }
-
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
- gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
-}
-
-static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
-{
- struct heartrate *hr = user_data;
-
- if (status) {
- error("Discover HRS characteristics failed: %s",
- att_ecode2str(status));
- return;
- }
-
- for (; chars; chars = chars->next) {
- struct gatt_char *c = chars->data;
-
- if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
- struct gatt_char *c_next =
- (chars->next ? chars->next->data : NULL);
-
- hr->attionotid = g_attrib_register(hr->attrib,
- ATT_OP_HANDLE_NOTIFY,
- c->value_handle,
- notify_handler, hr, NULL);
-
- discover_measurement_ccc(hr, c, c_next);
- } else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
- DBG("Body Sensor Location supported");
-
- gatt_read_char(hr->attrib, c->value_handle,
- read_sensor_location_cb, hr);
- } else if (g_strcmp0(c->uuid,
- HEART_RATE_CONTROL_POINT_UUID) == 0) {
- DBG("Heart Rate Control Point supported");
- hr->hrcp_val_handle = c->value_handle;
- }
- }
-}
-
-static void enable_measurement(gpointer data, gpointer user_data)
-{
- struct heartrate *hr = data;
- uint16_t handle = hr->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (hr->attrib == NULL || !handle)
- return;
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
- msg = g_strdup("Enable measurement");
-
- gatt_write_char(hr->attrib, handle, value, sizeof(value),
- char_write_cb, msg);
-}
-
-static void disable_measurement(gpointer data, gpointer user_data)
-{
- struct heartrate *hr = data;
- uint16_t handle = hr->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (hr->attrib == NULL || !handle)
- return;
-
- put_le16(0x0000, value);
- msg = g_strdup("Disable measurement");
-
- gatt_write_char(hr->attrib, handle, value, sizeof(value),
- char_write_cb, msg);
-}
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct heartrate *hr = user_data;
-
- DBG("");
-
- hr->attrib = g_attrib_ref(attrib);
-
- gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
- NULL, discover_char_cb, hr);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
- struct heartrate *hr = user_data;
-
- DBG("");
-
- if (hr->attionotid > 0) {
- g_attrib_unregister(hr->attrib, hr->attionotid);
- hr->attionotid = 0;
- }
-
- g_attrib_unref(hr->attrib);
- hr->attrib = NULL;
-}
-
-static void watcher_exit_cb(DBusConnection *conn, void *user_data)
-{
- struct watcher *watcher = user_data;
- struct heartrate_adapter *hradapter = watcher->hradapter;
-
- DBG("heartrate watcher [%s] disconnected", watcher->path);
-
- hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
- g_dbus_remove_watch(conn, watcher->id);
-
- if (g_slist_length(hradapter->watchers) == 0)
- g_slist_foreach(hradapter->devices, disable_measurement, 0);
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct heartrate_adapter *hradapter = data;
- struct watcher *watcher;
- const char *sender = dbus_message_get_sender(msg);
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(hradapter->watchers, sender, path);
- if (watcher != NULL)
- return btd_error_already_exists(msg);
-
- watcher = g_new0(struct watcher, 1);
- watcher->hradapter = hradapter;
- watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
- watcher, destroy_watcher);
- watcher->srv = g_strdup(sender);
- watcher->path = g_strdup(path);
-
- if (g_slist_length(hradapter->watchers) == 0)
- g_slist_foreach(hradapter->devices, enable_measurement, 0);
-
- hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
-
- DBG("heartrate watcher [%s] registered", path);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct heartrate_adapter *hradapter = data;
- struct watcher *watcher;
- const char *sender = dbus_message_get_sender(msg);
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(hradapter->watchers, sender, path);
- if (watcher == NULL)
- return btd_error_does_not_exist(msg);
-
- hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
- g_dbus_remove_watch(conn, watcher->id);
-
- if (g_slist_length(hradapter->watchers) == 0)
- g_slist_foreach(hradapter->devices, disable_measurement, 0);
-
- DBG("heartrate watcher [%s] unregistered", path);
-
- return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable heartrate_manager_methods[] = {
- { GDBUS_METHOD("RegisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- register_watcher) },
- { GDBUS_METHOD("UnregisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- unregister_watcher) },
- { }
-};
-
-static gboolean property_get_location(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct heartrate *hr = data;
- char *loc;
-
- if (!hr->has_location)
- return FALSE;
-
- loc = g_strdup(location2str(hr->location));
-
- if (loc == NULL)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
-
- g_free(loc);
-
- return TRUE;
-}
-
-static gboolean property_exists_location(const GDBusPropertyTable *property,
- void *data)
-{
- struct heartrate *hr = data;
-
- if (!hr->has_location || location2str(hr->location) == NULL)
- return FALSE;
-
- return TRUE;
-}
-
-static gboolean property_get_reset_supported(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct heartrate *hr = data;
- dbus_bool_t has_reset = !!hr->hrcp_val_handle;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset);
-
- return TRUE;
-}
-
-static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct heartrate *hr = data;
- uint8_t value;
- char *vmsg;
-
- if (!hr->hrcp_val_handle)
- return btd_error_not_supported(msg);
-
- if (!hr->attrib)
- return btd_error_not_available(msg);
-
- value = 0x01;
- vmsg = g_strdup("Reset Control Point");
- gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
- sizeof(value), char_write_cb, vmsg);
-
- DBG("Energy Expended Value has been reset");
-
- return dbus_message_new_method_return(msg);
-}
-
-static const GDBusMethodTable heartrate_device_methods[] = {
- { GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) },
- { }
-};
-
-static const GDBusPropertyTable heartrate_device_properties[] = {
- { "Location", "s", property_get_location, NULL,
- property_exists_location },
- { "ResetSupported", "b", property_get_reset_supported },
- { }
-};
-
-static int heartrate_adapter_register(struct btd_adapter *adapter)
-{
- struct heartrate_adapter *hradapter;
-
- hradapter = g_new0(struct heartrate_adapter, 1);
- hradapter->adapter = adapter;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- adapter_get_path(adapter),
- HEART_RATE_MANAGER_INTERFACE,
- heartrate_manager_methods,
- NULL, NULL, hradapter,
- destroy_heartrate_adapter)) {
- error("D-Bus failed to register %s interface",
- HEART_RATE_MANAGER_INTERFACE);
- destroy_heartrate_adapter(hradapter);
- return -EIO;
- }
-
- heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
-
- return 0;
-}
-
-static void heartrate_adapter_unregister(struct btd_adapter *adapter)
-{
- struct heartrate_adapter *hradapter;
-
- hradapter = find_heartrate_adapter(adapter);
- if (hradapter == NULL)
- return;
-
- heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- adapter_get_path(hradapter->adapter),
- HEART_RATE_MANAGER_INTERFACE);
-}
-
-static int heartrate_device_register(struct btd_device *device,
- struct gatt_primary *prim)
-{
- struct btd_adapter *adapter;
- struct heartrate_adapter *hradapter;
- struct heartrate *hr;
-
- adapter = device_get_adapter(device);
-
- hradapter = find_heartrate_adapter(adapter);
-
- if (hradapter == NULL)
- return -1;
-
- hr = g_new0(struct heartrate, 1);
- hr->dev = btd_device_ref(device);
- hr->hradapter = hradapter;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- device_get_path(device),
- HEART_RATE_INTERFACE,
- heartrate_device_methods,
- NULL,
- heartrate_device_properties,
- hr, destroy_heartrate)) {
- error("D-Bus failed to register %s interface",
- HEART_RATE_INTERFACE);
- destroy_heartrate(hr);
- return -EIO;
- }
-
- hr->svc_range = g_new0(struct att_range, 1);
- hr->svc_range->start = prim->range.start;
- hr->svc_range->end = prim->range.end;
-
- hradapter->devices = g_slist_prepend(hradapter->devices, hr);
-
- hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
- attio_disconnected_cb, hr);
-
- return 0;
-}
-
-static void heartrate_device_unregister(struct btd_device *device)
-{
- struct btd_adapter *adapter;
- struct heartrate_adapter *hradapter;
- struct heartrate *hr;
- GSList *l;
-
- adapter = device_get_adapter(device);
-
- hradapter = find_heartrate_adapter(adapter);
- if (hradapter == NULL)
- return;
-
- l = g_slist_find_custom(hradapter->devices, device, cmp_device);
- if (l == NULL)
- return;
-
- hr = l->data;
-
- hradapter->devices = g_slist_remove(hradapter->devices, hr);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(device), HEART_RATE_INTERFACE);
-}
-
-static int heartrate_adapter_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- return heartrate_adapter_register(adapter);
-}
-
-static void heartrate_adapter_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- heartrate_adapter_unregister(adapter);
-}
-
-static int heartrate_device_probe(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct gatt_primary *prim;
-
- prim = btd_device_get_primary(device, HEART_RATE_UUID);
- if (prim == NULL)
- return -EINVAL;
-
- return heartrate_device_register(device, prim);
-}
-
-static void heartrate_device_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
-
- heartrate_device_unregister(device);
-}
-
-static struct btd_profile hrp_profile = {
- .name = "Heart Rate GATT Driver",
- .remote_uuid = HEART_RATE_UUID,
-
- .device_probe = heartrate_device_probe,
- .device_remove = heartrate_device_remove,
-
- .adapter_probe = heartrate_adapter_probe,
- .adapter_remove = heartrate_adapter_remove,
-};
-
-static int heartrate_init(void)
-{
- return btd_profile_register(&hrp_profile);
-}
-
-static void heartrate_exit(void)
-{
- btd_profile_unregister(&hrp_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- heartrate_init, heartrate_exit)
diff --git a/profiles/input/hog-lib.c b/profiles/input/hog-lib.c
index 1df1799..e376c2b 100644
--- a/profiles/input/hog-lib.c
+++ b/profiles/input/hog-lib.c
@@ -315,8 +315,6 @@
error("bt_uhid_send: %s (%d)", strerror(-err), -err);
return;
}
-
- DBG("HoG report (%u bytes)", ev.u.input.size);
}
static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -1020,7 +1018,7 @@
return;
}
- strcpy((char *) ev.u.create.name, hog->name);
+ strncpy((char *) ev.u.create.name, hog->name, sizeof(ev.u.create.name));
ev.u.create.vendor = hog->vendor;
ev.u.create.product = hog->product;
ev.u.create.version = hog->version;
diff --git a/profiles/input/hog.c b/profiles/input/hog.c
index 837a1ad..233929d 100644
--- a/profiles/input/hog.c
+++ b/profiles/input/hog.c
@@ -54,7 +54,6 @@
#include "suspend.h"
#include "attrib/att.h"
#include "attrib/gattrib.h"
-#include "src/attio.h"
#include "attrib/gatt.h"
#include "hog-lib.h"
@@ -69,24 +68,6 @@
static gboolean suspend_supported = FALSE;
static struct queue *devices = NULL;
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct hog_device *dev = user_data;
-
- DBG("HoG connected");
-
- bt_hog_attach(dev->hog, attrib);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
- struct hog_device *dev = user_data;
-
- DBG("HoG disconnected");
-
- bt_hog_detach(dev->hog);
-}
-
static struct hog_device *hog_device_new(struct btd_device *device,
struct gatt_primary *prim)
{
@@ -115,15 +96,6 @@
dev->device = btd_device_ref(device);
- /*
- * TODO: Remove attio callback and use .accept once using
- * bt_gatt_client.
- */
- dev->attioid = btd_device_add_attio_callback(device,
- attio_connected_cb,
- attio_disconnected_cb,
- dev);
-
if (!devices)
devices = queue_new();
@@ -142,7 +114,6 @@
devices = NULL;
}
- btd_device_remove_attio_callback(dev->device, dev->attioid);
btd_device_unref(dev->device);
bt_hog_unref(dev->hog);
free(dev);
@@ -215,11 +186,39 @@
hog_device_free(dev);
}
+static int hog_accept(struct btd_service *service)
+{
+ struct hog_device *dev = btd_service_get_user_data(service);
+ struct btd_device *device = btd_service_get_device(service);
+ GAttrib *attrib = btd_device_get_attrib(device);
+
+ /* TODO: Replace GAttrib with bt_gatt_client */
+ bt_hog_attach(dev->hog, attrib);
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int hog_disconnect(struct btd_service *service)
+{
+ struct hog_device *dev = btd_service_get_user_data(service);
+
+ bt_hog_detach(dev->hog);
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
+}
+
static struct btd_profile hog_profile = {
.name = "input-hog",
.remote_uuid = HOG_UUID,
.device_probe = hog_probe,
.device_remove = hog_remove,
+ .accept = hog_accept,
+ .disconnect = hog_disconnect,
+ .auto_connect = true,
};
static int hog_init(void)
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 1bff9f8..e69ffaf 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -430,7 +430,6 @@
static void confirm_event(GIOChannel *chan, gpointer user_data)
{
struct network_adapter *na = user_data;
- struct network_server *ns;
bdaddr_t src, dst;
char address[18];
GError *err = NULL;
@@ -454,8 +453,7 @@
goto drop;
}
- ns = find_server(na->servers, BNEP_SVC_NAP);
- if (!ns || !ns->record_id || !ns->bridge)
+ if (!na->servers)
goto drop;
na->setup = g_new0(struct network_session, 1);
diff --git a/profiles/oad/oad.c b/profiles/oad/oad.c
index 08b8df4..ad09863 100644
--- a/profiles/oad/oad.c
+++ b/profiles/oad/oad.c
@@ -53,7 +53,6 @@
#include "src/service.h"
#include "attrib/att.h"
#include "attrib/gattrib.h"
-#include "src/attio.h"
#include "attrib/gatt.h"
#include "src/shared/util.h"
@@ -128,7 +127,6 @@
uint16_t id;
struct btd_device *device;
GAttrib *attrib;
- guint attioid;
struct gatt_primary *oad_primary;
struct oad_service identity;
@@ -758,11 +756,12 @@
}
}
-static void oad_attio_connected_cb(GAttrib *attrib, gpointer user_data)
+static int oad_accept(struct btd_service *service)
{
- struct oad_device *oaddev = user_data;
+ struct oad_device *oaddev = btd_service_get_user_data(service);
struct gatt_primary *prim = oaddev->oad_primary;
- GSList *l;
+ struct btd_device *device = btd_service_get_device(service);
+ GAttrib *attrib = btd_device_get_attrib(device);
DBG("OAD connected");
@@ -770,6 +769,8 @@
oaddev->identity.notify_enabled = 0;
oaddev->block.notify_enabled = 0;
+ btd_service_connecting_complete(service, 0);
+
if (oaddev->identity.ccc_handle == 0 || oaddev->block.ccc_handle == 0 ||
oaddev->identity.value_handle == 0 || oaddev->block.value_handle == 0) {
DBG("OAD discovering characteristics");
@@ -781,12 +782,13 @@
oad_enable_notify(oaddev);
}
}
+
+ return 0;
}
-static void oad_attio_disconnected_cb(gpointer user_data)
+static int oad_disconnect(struct btd_service *service)
{
- struct oad_device *oaddev = user_data;
- GSList *l;
+ struct oad_device *oaddev = btd_service_get_user_data(service);
DBG("OAD disconnected");
@@ -798,9 +800,14 @@
g_attrib_unregister(oaddev->attrib, oaddev->block.attio_id);
oaddev->block.attio_id = 0;
}
- if (oaddev->attrib)
+ if (oaddev->attrib) {
g_attrib_unref(oaddev->attrib);
- oaddev->attrib = NULL;
+ oaddev->attrib = NULL;
+ }
+
+ btd_service_disconnecting_complete(service, 0);
+
+ return 0;
}
static struct oad_device *oad_register_device(struct btd_device *device, struct gatt_primary *prim)
@@ -815,9 +822,6 @@
oaddev->oad_primary = g_memdup(prim, sizeof(*prim));
- oaddev->attioid = btd_device_add_attio_callback(device,
- oad_attio_connected_cb, oad_attio_disconnected_cb, oaddev);
-
return oaddev;
}
@@ -827,7 +831,6 @@
g_source_remove(oaddev->tid);
oaddev->tid = 0;
}
- btd_device_remove_attio_callback(oaddev->device, oaddev->attioid);
oad_free_device(oaddev);
return 0;
@@ -858,6 +861,7 @@
if (oaddev == NULL)
continue;
+ btd_service_set_user_data(service, oaddev);
oad_devices = g_slist_append(oad_devices, oaddev);
}
@@ -891,6 +895,9 @@
.remote_uuid = OAD_UUID,
.device_probe = oad_probe,
.device_remove = oad_remove,
+ .accept = oad_accept,
+ .disconnect = oad_disconnect,
+ .auto_connect = true,
};
static int oad_init(void)
diff --git a/profiles/scanparam/scan.c b/profiles/scanparam/scan.c
index d3ca762..0ff4a43 100644
--- a/profiles/scanparam/scan.c
+++ b/profiles/scanparam/scan.c
@@ -62,8 +62,6 @@
guint refresh_cb_id;
};
-static GSList *devices;
-
static void scan_free(struct scan *scan)
{
bt_gatt_client_unregister_notify(scan->client, scan->refresh_cb_id);
@@ -73,14 +71,6 @@
g_free(scan);
}
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct scan *scan = a;
- const struct btd_device *device = b;
-
- return scan->device == device ? 0 : -1;
-}
-
static void write_scan_params(struct scan *scan)
{
uint8_t value[4];
@@ -175,33 +165,32 @@
gatt_db_service_foreach_char(scan->attr, handle_characteristic, scan);
}
+static void scan_reset(struct scan *scan)
+{
+ scan->attr = NULL;
+ gatt_db_unref(scan->db);
+ scan->db = NULL;
+ bt_gatt_client_unref(scan->client);
+ scan->client = NULL;
+}
+
static int scan_param_accept(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct gatt_db *db = btd_device_get_gatt_db(device);
struct bt_gatt_client *client = btd_device_get_gatt_client(device);
bt_uuid_t scan_parameters_uuid;
- struct scan *scan;
- GSList *l;
+ struct scan *scan = btd_service_get_user_data(service);
char addr[18];
ba2str(device_get_address(device), addr);
DBG("Scan Parameters Client Driver profile accept (%s)", addr);
- l = g_slist_find_custom(devices, device, cmp_device);
- if (!l) {
+ if (!scan) {
error("Scan Parameters service not handled by profile");
return -1;
}
- scan = l->data;
-
- /* Clean-up any old client/db and acquire the new ones */
- scan->attr = NULL;
- gatt_db_unref(scan->db);
- bt_gatt_client_unref(scan->client);
-
-
scan->db = gatt_db_ref(db);
scan->client = bt_gatt_client_ref(client);
@@ -209,6 +198,25 @@
gatt_db_foreach_service(db, &scan_parameters_uuid,
foreach_scan_param_service, scan);
+ if (!scan->attr) {
+ error("Scan Parameters attribute not found");
+ scan_reset(scan);
+ return -1;
+ }
+
+ btd_service_connecting_complete(service, 0);
+
+ return 0;
+}
+
+static int scan_param_disconnect(struct btd_service *service)
+{
+ struct scan *scan = btd_service_get_user_data(service);
+
+ scan_reset(scan);
+
+ btd_service_disconnecting_complete(service, 0);
+
return 0;
}
@@ -216,21 +224,17 @@
{
struct btd_device *device = btd_service_get_device(service);
struct scan *scan;
- GSList *l;
char addr[18];
ba2str(device_get_address(device), addr);
DBG("GAP profile remove (%s)", addr);
- l = g_slist_find_custom(devices, device, cmp_device);
- if (!l) {
+ scan = btd_service_get_user_data(service);
+ if (!scan) {
error("GAP service not handled by profile");
return;
}
- scan = l->data;
-
- devices = g_slist_remove(devices, scan);
scan_free(scan);
}
@@ -238,16 +242,15 @@
{
struct btd_device *device = btd_service_get_device(service);
struct scan *scan;
- GSList *l;
char addr[18];
ba2str(device_get_address(device), addr);
DBG("Scan Parameters Client Driver profile probe (%s)", addr);
/* Ignore, if we were probed for this device already */
- l = g_slist_find_custom(devices, device, cmp_device);
- if (l) {
- error("Profile probed twice for the same device!");
+ scan = btd_service_get_user_data(service);
+ if (scan) {
+ error("Profile probed twice for the same service!");
return -1;
}
@@ -256,7 +259,7 @@
return -1;
scan->device = btd_device_ref(device);
- devices = g_slist_append(devices, scan);
+ btd_service_set_user_data(service, scan);
return 0;
}
@@ -266,6 +269,7 @@
.device_probe = scan_param_probe,
.device_remove = scan_param_remove,
.accept = scan_param_accept,
+ .disconnect = scan_param_disconnect,
};
static int scan_param_init(void)
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
deleted file mode 100644
index b0fc3e0..0000000
--- a/profiles/thermometer/thermometer.c
+++ /dev/null
@@ -1,1321 +0,0 @@
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- *
- * 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 <errno.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "gdbus/gdbus.h"
-
-#include "src/plugin.h"
-#include "src/dbus-common.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/service.h"
-#include "src/shared/util.h"
-#include "src/error.h"
-#include "src/log.h"
-#include "attrib/gattrib.h"
-#include "src/attio.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#define THERMOMETER_INTERFACE "org.bluez.Thermometer1"
-#define THERMOMETER_MANAGER_INTERFACE "org.bluez.ThermometerManager1"
-#define THERMOMETER_WATCHER_INTERFACE "org.bluez.ThermometerWatcher1"
-
-/* Temperature measurement flag fields */
-#define TEMP_UNITS 0x01
-#define TEMP_TIME_STAMP 0x02
-#define TEMP_TYPE 0x04
-
-#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */
-
-#define VALID_RANGE_DESC_SIZE 4
-#define TEMPERATURE_TYPE_SIZE 1
-#define MEASUREMENT_INTERVAL_SIZE 2
-
-struct thermometer_adapter {
- struct btd_adapter *adapter;
- GSList *devices;
- GSList *fwatchers; /* Final measurements */
- GSList *iwatchers; /* Intermediate measurements */
-};
-
-struct thermometer {
- struct btd_device *dev; /* Device reference */
- struct thermometer_adapter *tadapter;
- GAttrib *attrib; /* GATT connection */
- struct att_range *svc_range; /* Thermometer range */
- guint attioid; /* Att watcher id */
- /* attio id for Temperature Measurement value indications */
- guint attio_measurement_id;
- /* attio id for Intermediate Temperature value notifications */
- guint attio_intermediate_id;
- /* attio id for Measurement Interval value indications */
- guint attio_interval_id;
- gboolean intermediate;
- uint8_t type;
- uint16_t interval;
- uint16_t max;
- uint16_t min;
- gboolean has_type;
- gboolean has_interval;
-
- uint16_t measurement_ccc_handle;
- uint16_t intermediate_ccc_handle;
- uint16_t interval_val_handle;
-};
-
-struct characteristic {
- struct thermometer *t; /* Thermometer where the char belongs */
- char uuid[MAX_LEN_UUID_STR + 1];
-};
-
-struct watcher {
- struct thermometer_adapter *tadapter;
- guint id;
- char *srv;
- char *path;
-};
-
-struct measurement {
- struct thermometer *t;
- int16_t exp;
- int32_t mant;
- uint64_t time;
- gboolean suptime;
- char *unit;
- char *type;
- char *value;
-};
-
-struct tmp_interval_data {
- struct thermometer *thermometer;
- uint16_t interval;
-};
-
-static GSList *thermometer_adapters = NULL;
-
-static const char * const temp_type[] = {
- "<reserved>",
- "armpit",
- "body",
- "ear",
- "finger",
- "intestines",
- "mouth",
- "rectum",
- "toe",
- "tympanum"
-};
-
-static const char *temptype2str(uint8_t value)
-{
- if (value > 0 && value < G_N_ELEMENTS(temp_type))
- return temp_type[value];
-
- error("Temperature type %d reserved for future use", value);
- return NULL;
-}
-
-static void destroy_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_free(watcher->path);
- g_free(watcher->srv);
- g_free(watcher);
-}
-
-static void remove_watcher(gpointer user_data)
-{
- struct watcher *watcher = user_data;
-
- g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-}
-
-static void destroy_thermometer(gpointer user_data)
-{
- struct thermometer *t = user_data;
-
- if (t->attioid > 0)
- btd_device_remove_attio_callback(t->dev, t->attioid);
-
- if (t->attrib != NULL) {
- g_attrib_unregister(t->attrib, t->attio_measurement_id);
- g_attrib_unregister(t->attrib, t->attio_intermediate_id);
- g_attrib_unregister(t->attrib, t->attio_interval_id);
- g_attrib_unref(t->attrib);
- }
-
- btd_device_unref(t->dev);
- g_free(t->svc_range);
- g_free(t);
-}
-
-static void destroy_thermometer_adapter(gpointer user_data)
-{
- struct thermometer_adapter *tadapter = user_data;
-
- if (tadapter->devices != NULL)
- g_slist_free_full(tadapter->devices, destroy_thermometer);
-
- if (tadapter->fwatchers != NULL)
- g_slist_free_full(tadapter->fwatchers, remove_watcher);
-
- g_free(tadapter);
-}
-
-static int cmp_adapter(gconstpointer a, gconstpointer b)
-{
- const struct thermometer_adapter *tadapter = a;
- const struct btd_adapter *adapter = b;
-
- if (adapter == tadapter->adapter)
- return 0;
-
- return -1;
-}
-
-static int cmp_device(gconstpointer a, gconstpointer b)
-{
- const struct thermometer *t = a;
- const struct btd_device *dev = b;
-
- if (dev == t->dev)
- return 0;
-
- return -1;
-}
-
-static int cmp_watcher(gconstpointer a, gconstpointer b)
-{
- const struct watcher *watcher = a;
- const struct watcher *match = b;
- int ret;
-
- ret = g_strcmp0(watcher->srv, match->srv);
- if (ret != 0)
- return ret;
-
- return g_strcmp0(watcher->path, match->path);
-}
-
-static struct thermometer_adapter *
-find_thermometer_adapter(struct btd_adapter *adapter)
-{
- GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
- cmp_adapter);
- if (!l)
- return NULL;
-
- return l->data;
-}
-
-static void change_property(struct thermometer *t, const char *name,
- gpointer value) {
- if (g_strcmp0(name, "Intermediate") == 0) {
- gboolean *intermediate = value;
- if (t->intermediate == *intermediate)
- return;
-
- t->intermediate = *intermediate;
- } else if (g_strcmp0(name, "Interval") == 0) {
- uint16_t *interval = value;
- if (t->has_interval && t->interval == *interval)
- return;
-
- t->has_interval = TRUE;
- t->interval = *interval;
- } else if (g_strcmp0(name, "Maximum") == 0) {
- uint16_t *max = value;
- if (t->max == *max)
- return;
-
- t->max = *max;
- } else if (g_strcmp0(name, "Minimum") == 0) {
- uint16_t *min = value;
- if (t->min == *min)
- return;
-
- t->min = *min;
- } else {
- DBG("%s is not a thermometer property", name);
- return;
- }
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- device_get_path(t->dev),
- THERMOMETER_INTERFACE, name);
-}
-
-static void update_watcher(gpointer data, gpointer user_data)
-{
- struct watcher *w = data;
- struct measurement *m = user_data;
- const char *path = device_get_path(m->t->dev);
- DBusMessageIter iter;
- DBusMessageIter dict;
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(w->srv, w->path,
- THERMOMETER_WATCHER_INTERFACE,
- "MeasurementReceived");
- if (msg == NULL)
- return;
-
- dbus_message_iter_init_append(msg, &iter);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
-
- 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);
-
- dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
- dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
- dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
-
- if (m->suptime)
- dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
-
- dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
- dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(btd_get_dbus_connection(), msg);
-}
-
-static void recv_measurement(struct thermometer *t, struct measurement *m)
-{
- GSList *wlist;
-
- m->t = t;
-
- if (g_strcmp0(m->value, "intermediate") == 0)
- wlist = t->tadapter->iwatchers;
- else
- wlist = t->tadapter->fwatchers;
-
- g_slist_foreach(wlist, update_watcher, m);
-}
-
-static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
- uint16_t len, gboolean final)
-{
- struct measurement m;
- const char *type = NULL;
- uint8_t flags;
- uint32_t raw;
-
- /* skip opcode and handle */
- pdu += 3;
- len -= 3;
-
- if (len < 1) {
- DBG("Mandatory flags are not provided");
- return;
- }
-
- memset(&m, 0, sizeof(m));
-
- flags = *pdu;
-
- if (flags & TEMP_UNITS)
- m.unit = "fahrenheit";
- else
- m.unit = "celsius";
-
- pdu++;
- len--;
-
- if (len < 4) {
- DBG("Mandatory temperature measurement value is not provided");
- return;
- }
-
- raw = get_le32(pdu);
- m.mant = raw & 0x00FFFFFF;
- m.exp = ((int32_t) raw) >> 24;
-
- if (m.mant & 0x00800000) {
- /* convert to C2 negative value */
- m.mant = m.mant - FLOAT_MAX_MANTISSA;
- }
-
- pdu += 4;
- len -= 4;
-
- if (flags & TEMP_TIME_STAMP) {
- struct tm ts;
- time_t time;
-
- if (len < 7) {
- DBG("Time stamp is not provided");
- return;
- }
-
- ts.tm_year = get_le16(pdu) - 1900;
- ts.tm_mon = *(pdu + 2) - 1;
- ts.tm_mday = *(pdu + 3);
- ts.tm_hour = *(pdu + 4);
- ts.tm_min = *(pdu + 5);
- ts.tm_sec = *(pdu + 6);
- ts.tm_isdst = -1;
-
- time = mktime(&ts);
- m.time = (uint64_t) time;
- m.suptime = TRUE;
-
- pdu += 7;
- len -= 7;
- }
-
- if (flags & TEMP_TYPE) {
- if (len < 1) {
- DBG("Temperature type is not provided");
- return;
- }
-
- type = temptype2str(*pdu);
- } else if (t->has_type) {
- type = temptype2str(t->type);
- }
-
- m.type = g_strdup(type);
- m.value = final ? "final" : "intermediate";
-
- recv_measurement(t, &m);
- g_free(m.type);
-}
-
-
-static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
- uint8_t *opdu;
- uint16_t olen;
- size_t plen;
-
- if (len < 3) {
- DBG("Bad pdu received");
- return;
- }
-
- proc_measurement(t, pdu, len, TRUE);
-
- opdu = g_attrib_get_buffer(t->attrib, &plen);
- olen = enc_confirmation(opdu, plen);
-
- if (olen > 0)
- g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
-
- if (len < 3) {
- DBG("Bad pdu received");
- return;
- }
-
- proc_measurement(t, pdu, len, FALSE);
-}
-
-static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
- uint16_t interval;
- uint8_t *opdu;
- uint16_t olen;
- size_t plen;
-
- if (len < 5) {
- DBG("Bad pdu received");
- return;
- }
-
- interval = get_le16(pdu + 3);
- change_property(t, "Interval", &interval);
-
- opdu = g_attrib_get_buffer(t->attrib, &plen);
- olen = enc_confirmation(opdu, plen);
-
- if (olen > 0)
- g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
-}
-
-static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
- uint8_t value[VALID_RANGE_DESC_SIZE];
- uint16_t max, min;
- ssize_t vlen;
-
- if (status != 0) {
- DBG("Valid Range descriptor read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, value, sizeof(value));
- if (vlen < 0) {
- DBG("Protocol error\n");
- return;
- }
-
- if (vlen < 4) {
- DBG("Invalid range received");
- return;
- }
-
- min = get_le16(&value[0]);
- max = get_le16(&value[2]);
-
- if (min == 0 || min > max) {
- DBG("Invalid range");
- return;
- }
-
- change_property(t, "Maximum", &max);
- change_property(t, "Minimum", &min);
-}
-
-static void write_ccc_cb(guint8 status, const guint8 *pdu,
- guint16 len, gpointer user_data)
-{
- char *msg = user_data;
-
- if (status != 0)
- error("%s failed", msg);
-
- g_free(msg);
-}
-
-static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
- uint16_t handle)
-{
- uint8_t atval[2];
- uint16_t val;
- char *msg;
-
- if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
- if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
- gatt_read_char(ch->t->attrib, handle,
- valid_range_desc_cb, ch->t);
- return;
- }
-
- if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
- return;
-
- if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
- ch->t->measurement_ccc_handle = handle;
-
- if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
- val = 0x0000;
- msg = g_strdup("Disable Temperature Measurement ind");
- } else {
- val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
- msg = g_strdup("Enable Temperature Measurement ind");
- }
- } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
- ch->t->intermediate_ccc_handle = handle;
-
- if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
- val = 0x0000;
- msg = g_strdup("Disable Intermediate Temperature noti");
- } else {
- val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
- msg = g_strdup("Enable Intermediate Temperature noti");
- }
- } else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
- val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
- msg = g_strdup("Enable Measurement Interval indication");
- } else {
- return;
- }
-
- put_le16(val, atval);
- gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
- write_ccc_cb, msg);
-}
-
-static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
-{
- struct characteristic *ch = user_data;
-
- if (status != 0) {
- error("Discover all characteristic descriptors failed [%s]: %s",
- ch->uuid, att_ecode2str(status));
- goto done;
- }
-
- for ( ; descs; descs = descs->next) {
- struct gatt_desc *desc = descs->data;
-
- process_thermometer_desc(ch, desc->uuid16, desc->handle);
- }
-
-done:
- g_free(ch);
-}
-
-static void discover_desc(struct thermometer *t, struct gatt_char *c,
- struct gatt_char *c_next)
-{
- struct characteristic *ch;
- uint16_t start, end;
-
- start = c->value_handle + 1;
-
- if (c_next != NULL) {
- if (start == c_next->handle)
- return;
- end = c_next->handle - 1;
- } else if (c->value_handle != t->svc_range->end) {
- end = t->svc_range->end;
- } else {
- return;
- }
-
- ch = g_new0(struct characteristic, 1);
- ch->t = t;
- memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
-
- gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
-}
-
-static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
- uint8_t value[TEMPERATURE_TYPE_SIZE];
- ssize_t vlen;
-
- if (status != 0) {
- DBG("Temperature Type value read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, value, sizeof(value));
- if (vlen < 0) {
- DBG("Protocol error.");
- return;
- }
-
- if (vlen != 1) {
- DBG("Invalid length for Temperature type");
- return;
- }
-
- t->has_type = TRUE;
- t->type = value[0];
-}
-
-static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct thermometer *t = user_data;
- uint8_t value[MEASUREMENT_INTERVAL_SIZE];
- uint16_t interval;
- ssize_t vlen;
-
- if (status != 0) {
- DBG("Measurement Interval value read failed: %s",
- att_ecode2str(status));
- return;
- }
-
- vlen = dec_read_resp(pdu, len, value, sizeof(value));
- if (vlen < 0) {
- DBG("Protocol error\n");
- return;
- }
-
- if (vlen < 2) {
- DBG("Invalid Interval received");
- return;
- }
-
- interval = get_le16(&value[0]);
- change_property(t, "Interval", &interval);
-}
-
-static void process_thermometer_char(struct thermometer *t,
- struct gatt_char *c, struct gatt_char *c_next)
-{
- if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
- gboolean intermediate = TRUE;
- change_property(t, "Intermediate", &intermediate);
-
- t->attio_intermediate_id = g_attrib_register(t->attrib,
- ATT_OP_HANDLE_NOTIFY, c->value_handle,
- intermediate_notify_handler, t, NULL);
-
- discover_desc(t, c, c_next);
- } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
-
- t->attio_measurement_id = g_attrib_register(t->attrib,
- ATT_OP_HANDLE_IND, c->value_handle,
- measurement_ind_handler, t, NULL);
-
- discover_desc(t, c, c_next);
- } else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
- gatt_read_char(t->attrib, c->value_handle,
- read_temp_type_cb, t);
- } else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
- bool need_desc = false;
-
- gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
-
- if (c->properties & GATT_CHR_PROP_WRITE) {
- t->interval_val_handle = c->value_handle;
- need_desc = true;
- }
-
- if (c->properties & GATT_CHR_PROP_INDICATE) {
- t->attio_interval_id = g_attrib_register(t->attrib,
- ATT_OP_HANDLE_IND, c->value_handle,
- interval_ind_handler, t, NULL);
- need_desc = true;
- }
-
- if (need_desc)
- discover_desc(t, c, c_next);
- }
-}
-
-static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
- void *user_data)
-{
- struct thermometer *t = user_data;
- GSList *l;
-
- if (status != 0) {
- error("Discover thermometer characteristics: %s",
- att_ecode2str(status));
- return;
- }
-
- for (l = characteristics; l; l = l->next) {
- struct gatt_char *c = l->data;
- struct gatt_char *c_next = (l->next ? l->next->data : NULL);
-
- process_thermometer_char(t, c, c_next);
- }
-}
-
-static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct tmp_interval_data *data = user_data;
-
- if (status != 0) {
- error("Interval Write Request failed %s",
- att_ecode2str(status));
- goto done;
- }
-
- if (!dec_write_resp(pdu, len)) {
- error("Interval Write Request: protocol error");
- goto done;
- }
-
- change_property(data->thermometer, "Interval", &data->interval);
-
-done:
- g_free(user_data);
-}
-
-static void enable_final_measurement(gpointer data, gpointer user_data)
-{
- struct thermometer *t = data;
- uint16_t handle = t->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (t->attrib == NULL || !handle)
- return;
-
- put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
- msg = g_strdup("Enable Temperature Measurement indications");
-
- gatt_write_char(t->attrib, handle, value, sizeof(value),
- write_ccc_cb, msg);
-}
-
-static void enable_intermediate_measurement(gpointer data, gpointer user_data)
-{
- struct thermometer *t = data;
- uint16_t handle = t->intermediate_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (t->attrib == NULL || !handle)
- return;
-
- put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
- msg = g_strdup("Enable Intermediate Temperature notifications");
-
- gatt_write_char(t->attrib, handle, value, sizeof(value),
- write_ccc_cb, msg);
-}
-
-static void disable_final_measurement(gpointer data, gpointer user_data)
-{
- struct thermometer *t = data;
- uint16_t handle = t->measurement_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (t->attrib == NULL || !handle)
- return;
-
- put_le16(0x0000, value);
- msg = g_strdup("Disable Temperature Measurement indications");
-
- gatt_write_char(t->attrib, handle, value, sizeof(value),
- write_ccc_cb, msg);
-}
-
-static void disable_intermediate_measurement(gpointer data, gpointer user_data)
-{
- struct thermometer *t = data;
- uint16_t handle = t->intermediate_ccc_handle;
- uint8_t value[2];
- char *msg;
-
- if (t->attrib == NULL || !handle)
- return;
-
- put_le16(0x0000, value);
- msg = g_strdup("Disable Intermediate Temperature notifications");
-
- gatt_write_char(t->attrib, handle, value, sizeof(value),
- write_ccc_cb, msg);
-}
-
-static void remove_int_watcher(struct thermometer_adapter *tadapter,
- struct watcher *w)
-{
- if (!g_slist_find(tadapter->iwatchers, w))
- return;
-
- tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
-
- if (g_slist_length(tadapter->iwatchers) == 0)
- g_slist_foreach(tadapter->devices,
- disable_intermediate_measurement, 0);
-}
-
-static void watcher_exit(DBusConnection *conn, void *user_data)
-{
- struct watcher *watcher = user_data;
- struct thermometer_adapter *tadapter = watcher->tadapter;
-
- DBG("Thermometer watcher %s disconnected", watcher->path);
-
- remove_int_watcher(tadapter, watcher);
-
- tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
- g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-
- if (g_slist_length(tadapter->fwatchers) == 0)
- g_slist_foreach(tadapter->devices,
- disable_final_measurement, 0);
-}
-
-static struct watcher *find_watcher(GSList *list, const char *sender,
- const char *path)
-{
- struct watcher *match;
- GSList *l;
-
- match = g_new0(struct watcher, 1);
- match->srv = g_strdup(sender);
- match->path = g_strdup(path);
-
- l = g_slist_find_custom(list, match, cmp_watcher);
- destroy_watcher(match);
-
- if (l != NULL)
- return l->data;
-
- return NULL;
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct thermometer_adapter *tadapter = data;
- struct watcher *watcher;
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(tadapter->fwatchers, sender, path);
- if (watcher != NULL)
- return btd_error_already_exists(msg);
-
- DBG("Thermometer watcher %s registered", path);
-
- watcher = g_new0(struct watcher, 1);
- watcher->srv = g_strdup(sender);
- watcher->path = g_strdup(path);
- watcher->tadapter = tadapter;
- watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
- watcher, destroy_watcher);
-
- if (g_slist_length(tadapter->fwatchers) == 0)
- g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
-
- tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct thermometer_adapter *tadapter = data;
- struct watcher *watcher;
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(tadapter->fwatchers, sender, path);
- if (watcher == NULL)
- return btd_error_does_not_exist(msg);
-
- DBG("Thermometer watcher %s unregistered", path);
-
- remove_int_watcher(tadapter, watcher);
-
- tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
- g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
-
- if (g_slist_length(tadapter->fwatchers) == 0)
- g_slist_foreach(tadapter->devices,
- disable_final_measurement, 0);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct thermometer_adapter *ta = data;
- struct watcher *watcher;
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(ta->fwatchers, sender, path);
- if (watcher == NULL)
- return btd_error_does_not_exist(msg);
-
- if (find_watcher(ta->iwatchers, sender, path))
- return btd_error_already_exists(msg);
-
- DBG("Intermediate measurement watcher %s registered", path);
-
- if (g_slist_length(ta->iwatchers) == 0)
- g_slist_foreach(ta->devices,
- enable_intermediate_measurement, 0);
-
- ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct thermometer_adapter *ta = data;
- struct watcher *watcher;
- char *path;
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = find_watcher(ta->iwatchers, sender, path);
- if (watcher == NULL)
- return btd_error_does_not_exist(msg);
-
- DBG("Intermediate measurement %s unregistered", path);
-
- remove_int_watcher(ta, watcher);
-
- return dbus_message_new_method_return(msg);
-}
-
-static gboolean property_get_intermediate(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct thermometer *t = data;
- dbus_bool_t val;
-
- val = !!t->intermediate;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
-
- return TRUE;
-}
-
-static gboolean property_get_interval(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct thermometer *t = data;
-
- if (!t->has_interval)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
-
- return TRUE;
-}
-
-static void property_set_interval(const GDBusPropertyTable *property,
- DBusMessageIter *iter,
- GDBusPendingPropertySet id, void *data)
-{
- struct thermometer *t = data;
- struct tmp_interval_data *interval_data;
- uint16_t val;
- uint8_t atval[2];
-
- if (t->interval_val_handle == 0) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".NotSupported",
- "Operation is not supported");
- return;
- }
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- dbus_message_iter_get_basic(iter, &val);
-
- if (val < t->min || val > t->max) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- put_le16(val, &atval[0]);
-
- interval_data = g_new0(struct tmp_interval_data, 1);
- interval_data->thermometer = t;
- interval_data->interval = val;
- gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
- write_interval_cb, interval_data);
-
- g_dbus_pending_property_success(id);
-}
-
-static gboolean property_exists_interval(const GDBusPropertyTable *property,
- void *data)
-{
- struct thermometer *t = data;
-
- return t->has_interval;
-}
-
-static gboolean property_get_maximum(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct thermometer *t = data;
-
- if (!t->has_interval)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
-
- return TRUE;
-}
-
-static gboolean property_get_minimum(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct thermometer *t = data;
-
- if (!t->has_interval)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
-
- return TRUE;
-}
-
-static const GDBusPropertyTable thermometer_properties[] = {
- { "Intermediate", "b", property_get_intermediate },
- { "Interval", "q", property_get_interval, property_set_interval,
- property_exists_interval },
- { "Maximum", "q", property_get_maximum, NULL,
- property_exists_interval },
- { "Minimum", "q", property_get_minimum, NULL,
- property_exists_interval },
- { }
-};
-
-static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
-{
- struct thermometer *t = user_data;
-
- t->attrib = g_attrib_ref(attrib);
-
- gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
- NULL, configure_thermometer_cb, t);
-}
-
-static void attio_disconnected_cb(gpointer user_data)
-{
- struct thermometer *t = user_data;
-
- DBG("GATT Disconnected");
-
- if (t->attio_measurement_id > 0) {
- g_attrib_unregister(t->attrib, t->attio_measurement_id);
- t->attio_measurement_id = 0;
- }
-
- if (t->attio_intermediate_id > 0) {
- g_attrib_unregister(t->attrib, t->attio_intermediate_id);
- t->attio_intermediate_id = 0;
- }
-
- if (t->attio_interval_id > 0) {
- g_attrib_unregister(t->attrib, t->attio_interval_id);
- t->attio_interval_id = 0;
- }
-
- g_attrib_unref(t->attrib);
- t->attrib = NULL;
-}
-
-static int thermometer_register(struct btd_device *device,
- struct gatt_primary *tattr)
-{
- const char *path = device_get_path(device);
- struct thermometer *t;
- struct btd_adapter *adapter;
- struct thermometer_adapter *tadapter;
-
- adapter = device_get_adapter(device);
-
- tadapter = find_thermometer_adapter(adapter);
-
- if (tadapter == NULL)
- return -1;
-
- t = g_new0(struct thermometer, 1);
- t->dev = btd_device_ref(device);
- t->tadapter = tadapter;
- t->svc_range = g_new0(struct att_range, 1);
- t->svc_range->start = tattr->range.start;
- t->svc_range->end = tattr->range.end;
-
- tadapter->devices = g_slist_prepend(tadapter->devices, t);
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- path, THERMOMETER_INTERFACE,
- NULL, NULL, thermometer_properties,
- t, destroy_thermometer)) {
- error("D-Bus failed to register %s interface",
- THERMOMETER_INTERFACE);
- destroy_thermometer(t);
- return -EIO;
- }
-
- t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
- attio_disconnected_cb, t);
- return 0;
-}
-
-static void thermometer_unregister(struct btd_device *device)
-{
- struct thermometer *t;
- struct btd_adapter *adapter;
- struct thermometer_adapter *tadapter;
- GSList *l;
-
- adapter = device_get_adapter(device);
-
- tadapter = find_thermometer_adapter(adapter);
-
- if (tadapter == NULL)
- return;
-
- l = g_slist_find_custom(tadapter->devices, device, cmp_device);
- if (l == NULL)
- return;
-
- t = l->data;
-
- tadapter->devices = g_slist_remove(tadapter->devices, t);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- device_get_path(t->dev), THERMOMETER_INTERFACE);
-}
-
-static const GDBusMethodTable thermometer_manager_methods[] = {
- { GDBUS_METHOD("RegisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- register_watcher) },
- { GDBUS_METHOD("UnregisterWatcher",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- unregister_watcher) },
- { GDBUS_METHOD("EnableIntermediateMeasurement",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- enable_intermediate) },
- { GDBUS_METHOD("DisableIntermediateMeasurement",
- GDBUS_ARGS({ "agent", "o" }), NULL,
- disable_intermediate) },
- { }
-};
-
-static int thermometer_adapter_register(struct btd_adapter *adapter)
-{
- struct thermometer_adapter *tadapter;
-
- tadapter = g_new0(struct thermometer_adapter, 1);
- tadapter->adapter = adapter;
-
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- adapter_get_path(adapter),
- THERMOMETER_MANAGER_INTERFACE,
- thermometer_manager_methods,
- NULL, NULL, tadapter,
- destroy_thermometer_adapter)) {
- error("D-Bus failed to register %s interface",
- THERMOMETER_MANAGER_INTERFACE);
- destroy_thermometer_adapter(tadapter);
- return -EIO;
- }
-
- thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
-
- return 0;
-}
-
-static void thermometer_adapter_unregister(struct btd_adapter *adapter)
-{
- struct thermometer_adapter *tadapter;
-
- tadapter = find_thermometer_adapter(adapter);
- if (tadapter == NULL)
- return;
-
- thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
-
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- adapter_get_path(tadapter->adapter),
- THERMOMETER_MANAGER_INTERFACE);
-}
-
-static int thermometer_device_probe(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct gatt_primary *tattr;
-
- tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
- if (tattr == NULL)
- return -EINVAL;
-
- return thermometer_register(device, tattr);
-}
-
-static void thermometer_device_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
-
- thermometer_unregister(device);
-}
-
-static int thermometer_adapter_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- return thermometer_adapter_register(adapter);
-}
-
-static void thermometer_adapter_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- thermometer_adapter_unregister(adapter);
-}
-
-static struct btd_profile thermometer_profile = {
- .name = "Health Thermometer GATT driver",
- .remote_uuid = HEALTH_THERMOMETER_UUID,
- .device_probe = thermometer_device_probe,
- .device_remove = thermometer_device_remove,
- .adapter_probe = thermometer_adapter_probe,
- .adapter_remove = thermometer_adapter_remove
-};
-
-static int thermometer_init(void)
-{
- return btd_profile_register(&thermometer_profile);
-}
-
-static void thermometer_exit(void)
-{
- btd_profile_unregister(&thermometer_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- thermometer_init, thermometer_exit)
diff --git a/profiles/time/server.c b/profiles/time/server.c
deleted file mode 100644
index 2289c6a..0000000
--- a/profiles/time/server.c
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- *
- * 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 <time.h>
-#include <errno.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/profile.h"
-#include "src/plugin.h"
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-#include "attrib/att-database.h"
-#include "src/shared/util.h"
-#include "src/attrib-server.h"
-#include "attrib/gatt-service.h"
-#include "src/log.h"
-
-#define CURRENT_TIME_SVC_UUID 0x1805
-#define REF_TIME_UPDATE_SVC_UUID 0x1806
-
-#define LOCAL_TIME_INFO_CHR_UUID 0x2A0F
-#define TIME_UPDATE_CTRL_CHR_UUID 0x2A16
-#define TIME_UPDATE_STAT_CHR_UUID 0x2A17
-#define CT_TIME_CHR_UUID 0x2A2B
-
-enum {
- UPDATE_RESULT_SUCCESSFUL = 0,
- UPDATE_RESULT_CANCELED = 1,
- UPDATE_RESULT_NO_CONN = 2,
- UPDATE_RESULT_ERROR = 3,
- UPDATE_RESULT_TIMEOUT = 4,
- UPDATE_RESULT_NOT_ATTEMPTED = 5,
-};
-
-enum {
- UPDATE_STATE_IDLE = 0,
- UPDATE_STATE_PENDING = 1,
-};
-
-enum {
- GET_REFERENCE_UPDATE = 1,
- CANCEL_REFERENCE_UPDATE = 2,
-};
-
-static int encode_current_time(uint8_t value[10])
-{
- struct timespec tp;
- struct tm tm;
-
- if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
- int err = -errno;
-
- error("clock_gettime: %s", strerror(-err));
- return err;
- }
-
- if (localtime_r(&tp.tv_sec, &tm) == NULL) {
- error("localtime_r() failed");
- /* localtime_r() does not set errno */
- return -EINVAL;
- }
-
- put_le16(1900 + tm.tm_year, &value[0]); /* Year */
- value[2] = tm.tm_mon + 1; /* Month */
- value[3] = tm.tm_mday; /* Day */
- value[4] = tm.tm_hour; /* Hours */
- value[5] = tm.tm_min; /* Minutes */
- value[6] = tm.tm_sec; /* Seconds */
- value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
- /* From Time Profile spec: "The number of 1/256 fractions of a second."
- * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
- * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
- value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
- value[9] = 0x00; /* Adjust Reason */
-
- return 0;
-}
-
-static uint8_t current_time_read(struct attribute *a,
- struct btd_device *device, gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- uint8_t value[10];
-
- if (encode_current_time(value) < 0)
- return ATT_ECODE_IO;
-
- attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
- return 0;
-}
-
-static uint8_t local_time_info_read(struct attribute *a,
- struct btd_device *device, gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- uint8_t value[2];
-
- DBG("a=%p", a);
-
- tzset();
-
- /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
- * format (offset from UTC in number of 15 minutes increments). */
- value[0] = (uint8_t) (-1 * timezone / (60 * 15));
-
- /* FIXME: POSIX "daylight" variable only indicates whether there
- * is DST for the local time or not. The offset is unknown. */
- value[1] = daylight ? 0xff : 0x00;
-
- attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
- return 0;
-}
-
-static gboolean register_current_time_service(struct btd_adapter *adapter)
-{
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
-
- /* Current Time service */
- return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
- /* CT Time characteristic */
- GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
- GATT_CHR_PROP_NOTIFY,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- current_time_read, adapter,
-
- /* Local Time Information characteristic */
- GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- local_time_info_read, adapter,
-
- GATT_OPT_INVALID);
-}
-
-static uint8_t time_update_control(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- DBG("handle 0x%04x", a->handle);
-
- if (a->len != 1)
- DBG("Invalid control point value size: %zu", a->len);
-
- switch (a->data[0]) {
- case GET_REFERENCE_UPDATE:
- DBG("Get Reference Update");
- break;
- case CANCEL_REFERENCE_UPDATE:
- DBG("Cancel Reference Update");
- break;
- default:
- DBG("Unknown command: 0x%02x", a->data[0]);
- }
-
- return 0;
-}
-
-static uint8_t time_update_status(struct attribute *a,
- struct btd_device *device,
- gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
- uint8_t value[2];
-
- DBG("handle 0x%04x", a->handle);
-
- value[0] = UPDATE_STATE_IDLE;
- value[1] = UPDATE_RESULT_SUCCESSFUL;
- attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
-
- return 0;
-}
-
-static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
-{
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID);
-
- /* Reference Time Update service */
- return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
- /* Time Update control point */
- GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
- GATT_OPT_CHR_PROPS,
- GATT_CHR_PROP_WRITE_WITHOUT_RESP,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
- time_update_control, adapter,
-
- /* Time Update status */
- GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
- GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
- GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
- time_update_status, adapter,
-
- GATT_OPT_INVALID);
-}
-
-static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
-{
- const char *path = adapter_get_path(adapter);
-
- DBG("path %s", path);
-
- if (!register_current_time_service(adapter)) {
- error("Current Time Service could not be registered");
- return -EIO;
- }
-
- if (!register_ref_time_update_service(adapter)) {
- error("Reference Time Update Service could not be registered");
- return -EIO;
- }
-
- return 0;
-}
-
-static void time_server_exit(struct btd_profile *p,
- struct btd_adapter *adapter)
-{
- const char *path = adapter_get_path(adapter);
-
- DBG("path %s", path);
-}
-
-struct btd_profile time_profile = {
- .name = "gatt-time-server",
- .adapter_probe = time_server_init,
- .adapter_remove = time_server_exit,
-};
-
-static int time_init(void)
-{
- return btd_profile_register(&time_profile);
-}
-
-static void time_exit(void)
-{
- btd_profile_unregister(&time_profile);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- time_init, time_exit)
diff --git a/src/adapter.c b/src/adapter.c
index 3b21afa..e7cb86b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -3141,6 +3141,125 @@
return param;
}
+static int generate_and_write_irk(uint8_t *irk, GKeyFile *key_file,
+ const char *filename)
+{
+ struct bt_crypto *crypto;
+ char str_irk_out[33];
+ gsize length = 0;
+ char *str;
+ int i;
+
+ crypto = bt_crypto_new();
+ if (!crypto) {
+ error("Failed to open crypto");
+ return -1;
+ }
+
+ if (!bt_crypto_random_bytes(crypto, irk, 16)) {
+ error("Failed to generate IRK");
+ bt_crypto_unref(crypto);
+ return -1;
+ }
+
+ bt_crypto_unref(crypto);
+
+ for (i = 0; i < 16; i++)
+ sprintf(str_irk_out + (i * 2), "%02x", irk[i]);
+
+ str_irk_out[32] = '\0';
+ info("Generated IRK successfully");
+
+ g_key_file_set_string(key_file, "General", "IdentityResolvingKey",
+ str_irk_out);
+ str = g_key_file_to_data(key_file, &length, NULL);
+ g_file_set_contents(filename, str, length, NULL);
+ g_free(str);
+ DBG("Generated IRK written to file");
+ return 0;
+}
+
+static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
+{
+ char filename[PATH_MAX];
+ GKeyFile *key_file;
+ char address[18];
+ char *str_irk;
+ int ret;
+
+ ba2str(&adapter->bdaddr, address);
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity", address);
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ str_irk = g_key_file_get_string(key_file, "General",
+ "IdentityResolvingKey", NULL);
+ if (!str_irk) {
+ info("No IRK for %s, creating new IRK", address);
+ ret = generate_and_write_irk(irk, key_file, filename);
+ g_key_file_free(key_file);
+ return ret;
+ }
+
+ g_key_file_free(key_file);
+
+ if (strlen(str_irk) != 32 || str2buf(str_irk, irk, 16)) {
+ /* TODO re-create new IRK here? */
+ error("Invalid IRK format, disabling privacy");
+ g_free(str_irk);
+ return -1;
+ }
+
+ g_free(str_irk);
+ DBG("Successfully read IRK from file");
+ return 0;
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ btd_error(adapter->dev_id, "Failed to set privacy: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ DBG("Successfuly set privacy for index %u", adapter->dev_id);
+}
+
+static int set_privacy(struct btd_adapter *adapter, uint8_t privacy)
+{
+ struct mgmt_cp_set_privacy cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (privacy) {
+ uint8_t irk[16];
+
+ if (load_irk(adapter, irk) == 0) {
+ cp.privacy = privacy;
+ memcpy(cp.irk, irk, 16);
+ }
+ }
+
+ DBG("sending set privacy command for index %u", adapter->dev_id);
+ DBG("setting privacy mode 0x%02x for index %u", cp.privacy,
+ adapter->dev_id);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_privacy_complete, adapter, NULL) > 0)
+ return 0;
+
+ btd_error(adapter->dev_id, "Failed to set privacy for index %u",
+ adapter->dev_id);
+
+ return -1;
+}
+
static void load_link_keys_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -5488,8 +5607,11 @@
* supports this we can make the non-zero check conditional.
*/
if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
- !(eir_data.flags & EIR_BREDR_UNSUP))
+ !(eir_data.flags & EIR_BREDR_UNSUP)) {
device_set_bredr_support(dev);
+ /* Update last seen for BR/EDR in case its flag is set */
+ device_update_last_seen(dev, BDADDR_BREDR);
+ }
if (eir_data.name != NULL && eir_data.name_complete)
device_store_cached_name(dev, eir_data.name);
@@ -5549,6 +5671,9 @@
if (eir_data.sd_list)
device_set_service_data(dev, eir_data.sd_list);
+ if (bdaddr_type != BDADDR_BREDR)
+ device_set_flags(dev, eir_data.flags);
+
eir_data_free(&eir_data);
/*
@@ -6177,7 +6302,7 @@
return;
}
- err = device_confirm_passkey(device, btohl(ev->value),
+ err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
ev->confirm_hint);
if (err < 0) {
btd_error(adapter->dev_id,
@@ -6251,7 +6376,7 @@
return;
}
- err = device_request_passkey(device);
+ err = device_request_passkey(device, ev->addr.type);
if (err < 0) {
btd_error(adapter->dev_id,
"device_request_passkey: %s", strerror(-err));
@@ -6290,7 +6415,8 @@
DBG("passkey %06u entered %u", passkey, ev->entered);
- err = device_notify_passkey(device, passkey, ev->entered);
+ err = device_notify_passkey(device, ev->addr.type, passkey,
+ ev->entered);
if (err < 0)
btd_error(adapter->dev_id,
"device_notify_passkey: %s", strerror(-err));
@@ -7904,6 +8030,9 @@
if (missing_settings & MGMT_SETTING_SECURE_CONN)
set_mode(adapter, MGMT_OP_SET_SECURE_CONN, 0x01);
+ if (adapter->supported_settings & MGMT_SETTING_PRIVACY)
+ set_privacy(adapter, main_opts.privacy);
+
if (main_opts.fast_conn &&
(missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
@@ -8088,7 +8217,7 @@
adapter = btd_adapter_new(index);
if (!adapter) {
- btd_error(adapter->dev_id,
+ btd_error(index,
"Unable to create new adapter for index %u", index);
return;
}
diff --git a/src/advertising.c b/src/advertising.c
index d2019de..e5cd31c 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -288,7 +288,7 @@
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter value, entry;
+ DBusMessageIter value, entry, array;
uint16_t manuf_id;
uint8_t *manuf_data;
int len;
@@ -297,15 +297,17 @@
dbus_message_iter_get_basic(&entry, &manuf_id);
dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
- goto fail;
-
dbus_message_iter_recurse(&entry, &value);
- if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
goto fail;
- dbus_message_iter_get_fixed_array(&value, &manuf_data, &len);
+ dbus_message_iter_recurse(&value, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&array, &manuf_data, &len);
DBG("Adding ManufacturerData for %04x", manuf_id);
@@ -340,7 +342,7 @@
while (dbus_message_iter_get_arg_type(&entries)
== DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter value, entry;
+ DBusMessageIter value, entry, array;
const char *uuid_str;
bt_uuid_t uuid;
uint8_t *service_data;
@@ -353,15 +355,17 @@
goto fail;
dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_ARRAY)
- goto fail;
-
dbus_message_iter_recurse(&entry, &value);
- if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_BYTE)
+ if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
goto fail;
- dbus_message_iter_get_fixed_array(&value, &service_data, &len);
+ dbus_message_iter_recurse(&value, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_get_fixed_array(&array, &service_data, &len);
DBG("Adding ServiceData for %s", uuid_str);
diff --git a/src/device.c b/src/device.c
index 5198c6d..6ed9509 100644
--- a/src/device.c
+++ b/src/device.c
@@ -60,7 +60,6 @@
#include "adapter.h"
#include "gatt-database.h"
#include "attrib/gattrib.h"
-#include "attio.h"
#include "device.h"
#include "gatt-client.h"
#include "profile.h"
@@ -80,6 +79,7 @@
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 1
+#define INVALID_FLAGS 0xff
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -127,6 +127,7 @@
auth_type_t type;
struct agent *agent;
struct btd_device *device;
+ uint8_t addr_type;
uint32_t passkey;
char *pincode;
gboolean secure;
@@ -150,13 +151,6 @@
GSList *current;
};
-struct attio_data {
- guint id;
- attio_connect_cb cfunc;
- attio_disconnect_cb dcfunc;
- gpointer user_data;
-};
-
struct svc_callback {
unsigned int id;
guint idle_id;
@@ -165,15 +159,6 @@
void *user_data;
};
-typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data);
-typedef void (*attio_success_cb) (gpointer user_data);
-
-struct att_callbacks {
- attio_error_cb err; /* Callback for error */
- attio_success_cb success; /* Callback for success */
- gpointer user_data;
-};
-
/* Per-bearer (LE or BR/EDR) device state */
struct bearer_state {
bool paired;
@@ -200,6 +185,7 @@
GSList *svc_callbacks;
GSList *eir_uuids;
struct bt_ad *ad;
+ uint8_t ad_flags[1];
char name[MAX_NAME_LENGTH + 1];
char *alias;
uint32_t class;
@@ -225,8 +211,6 @@
DBusMessage *connect; /* connect message */
DBusMessage *disconnect; /* disconnect message */
GAttrib *attrib;
- GSList *attios;
- GSList *attios_offline;
struct bt_att *att; /* The new ATT transport */
uint16_t att_mtu; /* The ATT MTU */
@@ -625,8 +609,6 @@
g_slist_free_full(device->uuids, g_free);
g_slist_free_full(device->primaries, g_free);
- g_slist_free_full(device->attios, g_free);
- g_slist_free_full(device->attios_offline, g_free);
g_slist_free_full(device->svc_callbacks, svc_dev_remove);
/* Reset callbacks since the device is going to be freed */
@@ -964,6 +946,31 @@
return TRUE;
}
+static gboolean dev_property_flags_exist(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct btd_device *device = data;
+
+ return device->ad_flags[0] != INVALID_FLAGS;
+}
+
+static gboolean
+dev_property_get_flags(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ uint8_t *flags = device->ad_flags;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &flags, sizeof(device->ad_flags));
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
static gboolean dev_property_get_trusted(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -1786,6 +1793,16 @@
time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME;
time_t current = time(NULL);
+ /* Prefer bonded bearer in case only one is bonded */
+ if (dev->bredr_state.bonded && !dev->le_state.bonded )
+ return BDADDR_BREDR;
+ else if (!dev->bredr_state.bonded && dev->le_state.bonded)
+ return dev->bdaddr_type;
+
+ /* If the address is random it can only be connected over LE */
+ if (dev->bdaddr_type == BDADDR_LE_RANDOM)
+ return dev->bdaddr_type;
+
if (dev->bredr_seen) {
bredr_last = current - dev->bredr_seen;
if (bredr_last > SEEN_TRESHHOLD)
@@ -1807,7 +1824,11 @@
if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME))
return dev->bdaddr_type;
- if (bredr_last < le_last)
+ /*
+ * Prefer BR/EDR if time is the same since it might be from an
+ * advertisement with BR/EDR flag set.
+ */
+ if (bredr_last <= le_last)
return BDADDR_BREDR;
return dev->bdaddr_type;
@@ -1819,9 +1840,18 @@
struct btd_device *dev = user_data;
uint8_t bdaddr_type;
- if (dev->bredr_state.connected)
- bdaddr_type = dev->bdaddr_type;
- else if (dev->le_state.connected && dev->bredr)
+ if (dev->bredr_state.connected) {
+ /*
+ * Check if services have been resolved and there is at list
+ * one connected before switching to connect LE.
+ */
+ if (dev->bredr_state.svc_resolved &&
+ find_service_with_state(dev->services,
+ BTD_SERVICE_STATE_CONNECTED))
+ bdaddr_type = dev->bdaddr_type;
+ else
+ bdaddr_type = BDADDR_BREDR;
+ } else if (dev->le_state.connected && dev->bredr)
bdaddr_type = BDADDR_BREDR;
else
bdaddr_type = select_conn_bearer(dev);
@@ -2079,6 +2109,7 @@
sprintf(handle, "%04hx", handle_num);
+ gatt_db_attribute_get_service_uuid(service, &uuid);
bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
sprintf(value, GATT_INCLUDE_UUID_STR ":%04hx:%04hx:%s", start,
end, uuid_str);
@@ -2355,6 +2386,35 @@
}
}
+static void bonding_request_free(struct bonding_req *bonding)
+{
+ if (!bonding)
+ return;
+
+ if (bonding->listener_id)
+ g_dbus_remove_watch(dbus_conn, bonding->listener_id);
+
+ if (bonding->msg)
+ dbus_message_unref(bonding->msg);
+
+ if (bonding->cb_iter)
+ g_free(bonding->cb_iter);
+
+ if (bonding->agent) {
+ agent_cancel(bonding->agent);
+ agent_unref(bonding->agent);
+ bonding->agent = NULL;
+ }
+
+ if (bonding->retry_timer)
+ g_source_remove(bonding->retry_timer);
+
+ if (bonding->device)
+ bonding->device->bonding = NULL;
+
+ g_free(bonding);
+}
+
static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -2425,8 +2485,10 @@
BDADDR_BREDR, io_cap);
}
- if (err < 0)
+ if (err < 0) {
+ bonding_request_free(device->bonding);
return btd_error_failed(msg, strerror(-err));
+ }
return NULL;
}
@@ -2467,35 +2529,6 @@
}
}
-static void bonding_request_free(struct bonding_req *bonding)
-{
- if (!bonding)
- return;
-
- if (bonding->listener_id)
- g_dbus_remove_watch(dbus_conn, bonding->listener_id);
-
- if (bonding->msg)
- dbus_message_unref(bonding->msg);
-
- if (bonding->cb_iter)
- g_free(bonding->cb_iter);
-
- if (bonding->agent) {
- agent_cancel(bonding->agent);
- agent_unref(bonding->agent);
- bonding->agent = NULL;
- }
-
- if (bonding->retry_timer)
- g_source_remove(bonding->retry_timer);
-
- if (bonding->device)
- bonding->device->bonding = NULL;
-
- g_free(bonding);
-}
-
static void device_cancel_bonding(struct btd_device *device, uint8_t status)
{
struct bonding_req *bonding = device->bonding;
@@ -2568,16 +2601,15 @@
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
{ "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
- NULL, dev_property_manufacturer_data_exist,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ NULL, dev_property_manufacturer_data_exist },
{ "ServiceData", "a{sv}", dev_property_get_service_data,
- NULL, dev_property_service_data_exist,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ NULL, dev_property_service_data_exist },
{ "TxPower", "n", dev_property_get_tx_power, NULL,
- dev_property_exists_tx_power,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ dev_property_exists_tx_power },
+ { "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL },
+ { "AdvertisingFlags", "ay", dev_property_get_flags, NULL,
+ dev_property_flags_exist,
+ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL},
{ }
};
@@ -3598,6 +3630,8 @@
return NULL;
}
+ memset(device->ad_flags, INVALID_FLAGS, sizeof(device->ad_flags));
+
device->ad = bt_ad_new();
if (!device->ad) {
device_free(device);
@@ -4119,7 +4153,10 @@
return NULL;
}
- if (profile->auto_connect)
+ /* Only set auto connect if profile has set the flag and can really
+ * accept connections.
+ */
+ if (profile->auto_connect && profile->accept)
device_set_auto_connect(device, TRUE);
return service;
@@ -4568,25 +4605,16 @@
return device->auto_connect;
}
-static void attio_connected(gpointer data, gpointer user_data)
+static void disconnect_gatt_service(gpointer data, gpointer user_data)
{
- struct attio_data *attio = data;
- GAttrib *attrib = user_data;
+ struct btd_service *service = data;
+ struct btd_profile *profile = btd_service_get_profile(service);
- DBG("");
+ /* Ignore if profile cannot accept connections */
+ if (!profile->accept)
+ return;
- if (attio->cfunc)
- attio->cfunc(attrib, attio->user_data);
-}
-
-static void attio_disconnected(gpointer data, gpointer user_data)
-{
- struct attio_data *attio = data;
-
- DBG("");
-
- if (attio->dcfunc)
- attio->dcfunc(attio->user_data);
+ btd_service_disconnect(service);
}
static void att_disconnected_cb(int err, void *user_data)
@@ -4600,7 +4628,7 @@
DBG("%s (%d)", strerror(err), err);
- g_slist_foreach(device->attios, attio_disconnected, NULL);
+ g_slist_foreach(device->services, disconnect_gatt_service, NULL);
btd_gatt_client_disconnected(device->client_dbus);
@@ -4696,9 +4724,6 @@
bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL);
- /* Notify attio so it can react to notifications */
- g_slist_foreach(device->attios, attio_connected, device->attrib);
-
/*
* Notify notify existing service about the new connection so they can
* react to notifications while discovering services
@@ -4804,7 +4829,7 @@
}
dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
- attrib = g_attrib_new(io, dev->att_mtu, false);
+ attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false);
if (!attrib) {
error("Unable to create new GAttrib instance");
return false;
@@ -4835,12 +4860,12 @@
dst = device_get_address(dev);
ba2str(dst, dstaddr);
- gatt_client_init(dev);
- gatt_server_init(dev, btd_gatt_database_get_db(database));
-
if (gatt_db_isempty(dev->db))
load_gatt_db(dev, srcaddr, dstaddr);
+ gatt_client_init(dev);
+ gatt_server_init(dev, btd_gatt_database_get_db(database));
+
/*
* Remove the device from the connect_list and give the passive
* scanning another chance to be restarted in case there are
@@ -4853,8 +4878,7 @@
static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
- struct att_callbacks *attcb = user_data;
- struct btd_device *device = attcb->user_data;
+ struct btd_device *device = user_data;
DBusMessage *reply;
uint8_t io_cap;
int err = 0;
@@ -4865,8 +4889,20 @@
if (gerr) {
DBG("%s", gerr->message);
- if (attcb->err)
- attcb->err(gerr, user_data);
+ if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
+ goto done;
+
+ if (device_get_auto_connect(device)) {
+ DBG("Enabling automatic connections");
+ adapter_connect_list_add(device->adapter, device);
+ }
+
+ if (device->browse) {
+ browse_request_complete(device->browse,
+ device->bdaddr_type,
+ -ECONNABORTED);
+ device->browse = NULL;
+ }
err = -ECONNABORTED;
goto done;
@@ -4875,9 +4911,6 @@
if (!device_attach_att(device, io))
goto done;
- if (attcb->success)
- attcb->success(user_data);
-
if (!device->bonding)
goto done;
@@ -4910,28 +4943,11 @@
dbus_message_unref(device->connect);
device->connect = NULL;
}
-
- g_free(attcb);
-}
-
-static void att_error_cb(const GError *gerr, gpointer user_data)
-{
- struct att_callbacks *attcb = user_data;
- struct btd_device *device = attcb->user_data;
-
- if (g_error_matches(gerr, BT_IO_ERROR, ECONNABORTED))
- return;
-
- if (device_get_auto_connect(device)) {
- DBG("Enabling automatic connections");
- adapter_connect_list_add(device->adapter, device);
- }
}
int device_connect_le(struct btd_device *dev)
{
struct btd_adapter *adapter = dev->adapter;
- struct att_callbacks *attcb;
BtIOSecLevel sec_level;
GIOChannel *io;
GError *gerr = NULL;
@@ -4945,10 +4961,6 @@
DBG("Connection attempt to: %s", addr);
- attcb = g_new0(struct att_callbacks, 1);
- attcb->err = att_error_cb;
- attcb->user_data = dev;
-
if (dev->le_state.paired)
sec_level = BT_IO_SEC_MEDIUM;
else
@@ -4958,7 +4970,7 @@
* This connection will help us catch any PDUs that comes before
* pairing finishes
*/
- io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
+ io = bt_io_connect(att_connect_cb, dev, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(adapter),
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
@@ -4980,7 +4992,6 @@
error("ATT bt_io_connect(%s): %s", addr, gerr->message);
g_error_free(gerr);
- g_free(attcb);
return -EIO;
}
@@ -4990,21 +5001,6 @@
return 0;
}
-static void att_browse_error_cb(const GError *gerr, gpointer user_data)
-{
- struct att_callbacks *attcb = user_data;
- struct btd_device *device = attcb->user_data;
- struct browse_req *req = device->browse;
-
- device->browse = NULL;
- browse_request_complete(req, device->bdaddr_type, -ECONNABORTED);
-}
-
-static void att_browse_cb(gpointer user_data)
-{
- DBG("ATT connection successful");
-}
-
static struct browse_req *browse_request_new(struct btd_device *device,
DBusMessage *msg)
{
@@ -5038,7 +5034,6 @@
static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
{
struct btd_adapter *adapter = device->adapter;
- struct att_callbacks *attcb;
struct browse_req *req;
req = browse_request_new(device, msg);
@@ -5061,13 +5056,8 @@
return 0;
}
- attcb = g_new0(struct att_callbacks, 1);
- attcb->err = att_browse_error_cb;
- attcb->success = att_browse_cb;
- attcb->user_data = device;
-
device->att_io = bt_io_connect(att_connect_cb,
- attcb, NULL, NULL,
+ device, NULL, NULL,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(adapter),
BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
@@ -5080,7 +5070,6 @@
if (device->att_io == NULL) {
device->browse = NULL;
browse_request_free(req);
- g_free(attcb);
return -EIO;
}
@@ -5336,6 +5325,22 @@
DEVICE_INTERFACE, "TxPower");
}
+void device_set_flags(struct btd_device *device, uint8_t flags)
+{
+ if (!device)
+ return;
+
+ DBG("flags %d", flags);
+
+ if (device->ad_flags[0] == flags)
+ return;
+
+ device->ad_flags[0] = flags;
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "AdvertisingFlags");
+}
+
static gboolean start_discovery(gpointer user_data)
{
struct btd_device *device = user_data;
@@ -5691,7 +5696,7 @@
return;
btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
- device->bdaddr_type,
+ auth->addr_type,
err ? FALSE : TRUE);
agent_unref(device->authr->agent);
@@ -5712,7 +5717,7 @@
passkey = INVALID_PASSKEY;
btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
- device->bdaddr_type, passkey);
+ auth->addr_type, passkey);
agent_unref(device->authr->agent);
device->authr->agent = NULL;
@@ -5730,7 +5735,9 @@
}
static struct authentication_req *new_auth(struct btd_device *device,
- auth_type_t type, gboolean secure)
+ uint8_t addr_type,
+ auth_type_t type,
+ gboolean secure)
{
struct authentication_req *auth;
struct agent *agent;
@@ -5758,6 +5765,7 @@
auth->agent = agent;
auth->device = device;
auth->type = type;
+ auth->addr_type = addr_type;
auth->secure = secure;
device->authr = auth;
@@ -5769,7 +5777,7 @@
struct authentication_req *auth;
int err;
- auth = new_auth(device, AUTH_TYPE_PINCODE, secure);
+ auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_PINCODE, secure);
if (!auth)
return -EPERM;
@@ -5783,12 +5791,12 @@
return err;
}
-int device_request_passkey(struct btd_device *device)
+int device_request_passkey(struct btd_device *device, uint8_t type)
{
struct authentication_req *auth;
int err;
- auth = new_auth(device, AUTH_TYPE_PASSKEY, FALSE);
+ auth = new_auth(device, type, AUTH_TYPE_PASSKEY, FALSE);
if (!auth)
return -EPERM;
@@ -5802,14 +5810,13 @@
return err;
}
-int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
- uint8_t confirm_hint)
-
+int device_confirm_passkey(struct btd_device *device, uint8_t type,
+ int32_t passkey, uint8_t confirm_hint)
{
struct authentication_req *auth;
int err;
- auth = new_auth(device, AUTH_TYPE_CONFIRM, FALSE);
+ auth = new_auth(device, type, AUTH_TYPE_CONFIRM, FALSE);
if (!auth)
return -EPERM;
@@ -5830,8 +5837,8 @@
return err;
}
-int device_notify_passkey(struct btd_device *device, uint32_t passkey,
- uint8_t entered)
+int device_notify_passkey(struct btd_device *device, uint8_t type,
+ uint32_t passkey, uint8_t entered)
{
struct authentication_req *auth;
int err;
@@ -5841,7 +5848,7 @@
if (auth->type != AUTH_TYPE_NOTIFY_PASSKEY)
return -EPERM;
} else {
- auth = new_auth(device, AUTH_TYPE_NOTIFY_PASSKEY, FALSE);
+ auth = new_auth(device, type, AUTH_TYPE_NOTIFY_PASSKEY, FALSE);
if (!auth)
return -EPERM;
}
@@ -5861,7 +5868,7 @@
struct authentication_req *auth;
int err;
- auth = new_auth(device, AUTH_TYPE_NOTIFY_PINCODE, secure);
+ auth = new_auth(device, BDADDR_BREDR, AUTH_TYPE_NOTIFY_PINCODE, secure);
if (!auth)
return -EPERM;
@@ -5970,6 +5977,14 @@
return device->client;
}
+void *btd_device_get_attrib(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->attrib;
+}
+
struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
{
if (!device)
@@ -6109,91 +6124,6 @@
store_device_info(device);
}
-static gboolean notify_attios(gpointer user_data)
-{
- struct btd_device *device = user_data;
-
- DBG("");
-
- if (device->attrib == NULL)
- return FALSE;
-
- g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
- device->attios = g_slist_concat(device->attios, device->attios_offline);
- device->attios_offline = NULL;
-
- return FALSE;
-}
-
-guint btd_device_add_attio_callback(struct btd_device *device,
- attio_connect_cb cfunc,
- attio_disconnect_cb dcfunc,
- gpointer user_data)
-{
- struct attio_data *attio;
- static guint attio_id = 0;
-
- DBG("%p registered ATT connection callback", device);
-
- attio = g_new0(struct attio_data, 1);
- attio->id = ++attio_id;
- attio->cfunc = cfunc;
- attio->dcfunc = dcfunc;
- attio->user_data = user_data;
-
- device_set_auto_connect(device, TRUE);
-
- /* Check if there is no GAttrib associated to the device created by a
- * incoming connection */
- if (!device->attrib)
- device->attrib = attrib_from_device(device);
-
- if (device->attrib && cfunc) {
- device->attios_offline = g_slist_append(device->attios_offline,
- attio);
- g_idle_add(notify_attios, device);
- return attio->id;
- }
-
- device->attios = g_slist_append(device->attios, attio);
-
- return attio->id;
-}
-
-static int attio_id_cmp(gconstpointer a, gconstpointer b)
-{
- const struct attio_data *attio = a;
- guint id = GPOINTER_TO_UINT(b);
-
- return attio->id - id;
-}
-
-gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
-{
- struct attio_data *attio;
- GSList *l;
-
- l = g_slist_find_custom(device->attios, GUINT_TO_POINTER(id),
- attio_id_cmp);
- if (l) {
- attio = l->data;
- device->attios = g_slist_remove(device->attios, attio);
- } else {
- l = g_slist_find_custom(device->attios_offline,
- GUINT_TO_POINTER(id), attio_id_cmp);
- if (!l)
- return FALSE;
-
- attio = l->data;
- device->attios_offline = g_slist_remove(device->attios_offline,
- attio);
- }
-
- g_free(attio);
-
- return TRUE;
-}
-
void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
uint16_t vendor, uint16_t product, uint16_t version)
{
diff --git a/src/device.h b/src/device.h
index 35f0e96..bc5535c 100644
--- a/src/device.h
+++ b/src/device.h
@@ -70,6 +70,7 @@
struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
+void *btd_device_get_attrib(struct btd_device *device);
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end);
bool device_attach_att(struct btd_device *dev, GIOChannel *io);
@@ -96,6 +97,7 @@
int8_t delta_threshold);
void device_set_rssi(struct btd_device *device, int8_t rssi);
void device_set_tx_power(struct btd_device *device, int8_t tx_power);
+void device_set_flags(struct btd_device *device, uint8_t flags);
bool btd_device_is_connected(struct btd_device *dev);
uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
bool device_is_retrying(struct btd_device *device);
@@ -109,11 +111,11 @@
long device_bonding_last_duration(struct btd_device *device);
void device_bonding_restart_timer(struct btd_device *device);
int device_request_pincode(struct btd_device *device, gboolean secure);
-int device_request_passkey(struct btd_device *device);
-int device_confirm_passkey(struct btd_device *device, uint32_t passkey,
- uint8_t confirm_hint);
-int device_notify_passkey(struct btd_device *device, uint32_t passkey,
- uint8_t entered);
+int device_request_passkey(struct btd_device *device, uint8_t type);
+int device_confirm_passkey(struct btd_device *device, uint8_t type,
+ int32_t passkey, uint8_t confirm_hint);
+int device_notify_passkey(struct btd_device *device, uint8_t type,
+ uint32_t passkey, uint8_t entered);
int device_notify_pincode(struct btd_device *device, gboolean secure,
const char *pincode);
void device_cancel_authentication(struct btd_device *device, gboolean aborted);
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 7abb306..114981c 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -184,7 +184,7 @@
{
gboolean *ret = user_data;
- if (err || length == 0) {
+ if (err) {
*ret = FALSE;
return;
}
@@ -297,7 +297,7 @@
}
static void async_dbus_op_reply(struct async_dbus_op *op, int err,
- const uint8_t *value, size_t length)
+ const uint8_t *value, ssize_t length)
{
const struct queue_entry *entry;
DBusMessage *reply;
@@ -309,7 +309,7 @@
if (err) {
reply = err > 0 ? create_gatt_dbus_error(msg, err) :
- btd_error_failed(msg, strerror(err));
+ btd_error_failed(msg, strerror(-err));
goto send_reply;
}
@@ -319,7 +319,7 @@
return;
}
- if (value)
+ if (length >= 0)
message_append_byte_array(reply, value, length);
send_reply:
@@ -489,7 +489,7 @@
}
done:
- async_dbus_op_reply(op, err, NULL, 0);
+ async_dbus_op_reply(op, err, NULL, -1);
}
static void write_cb(bool success, uint8_t att_ecode, void *user_data)
@@ -613,22 +613,17 @@
}
static const GDBusPropertyTable descriptor_properties[] = {
- { "UUID", "s", descriptor_get_uuid, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "UUID", "s", descriptor_get_uuid },
+ { "Characteristic", "o", descriptor_get_characteristic, },
+ { "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists },
{ }
};
static const GDBusMethodTable descriptor_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
- GDBUS_ARGS({ "options", "a{sv}" }),
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
GDBUS_ARGS({ "value", "ay" }),
descriptor_read_value) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
{ "options", "a{sv}" }),
NULL,
descriptor_write_value) },
@@ -1131,7 +1126,7 @@
else
err = att_ecode ? att_ecode : -ENOENT;
- async_dbus_op_reply(op, err, NULL, 0);
+ async_dbus_op_reply(op, err, NULL, -1);
}
static void register_notify_cb(uint16_t att_ecode, void *user_data)
@@ -1251,34 +1246,27 @@
}
static const GDBusPropertyTable characteristic_properties[] = {
- { "UUID", "s", characteristic_get_uuid, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Service", "o", characteristic_get_service, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "UUID", "s", characteristic_get_uuid, NULL, NULL },
+ { "Service", "o", characteristic_get_service, NULL, NULL },
{ "Value", "ay", characteristic_get_value, NULL,
- characteristic_value_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ characteristic_value_exists },
{ "Notifying", "b", characteristic_get_notifying, NULL,
- characteristic_notifying_exists,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Flags", "as", characteristic_get_flags, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ characteristic_notifying_exists },
+ { "Flags", "as", characteristic_get_flags, NULL, NULL },
{ }
};
static const GDBusMethodTable characteristic_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue",
- GDBUS_ARGS({ "options", "a{sv}" }),
+ { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
GDBUS_ARGS({ "value", "ay" }),
characteristic_read_value) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue",
- GDBUS_ARGS({ "value", "ay" },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
{ "options", "a{sv}" }),
NULL,
characteristic_write_value) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL,
+ { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
characteristic_start_notify) },
- { GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
+ { GDBUS_METHOD("StopNotify", NULL, NULL,
characteristic_stop_notify) },
{ }
};
@@ -1411,12 +1399,9 @@
}
static const GDBusPropertyTable service_properties[] = {
- { "UUID", "s", service_get_uuid, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Device", "o", service_get_device, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
- { "Primary", "b", service_get_primary, NULL, NULL,
- G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+ { "UUID", "s", service_get_uuid },
+ { "Device", "o", service_get_device },
+ { "Primary", "b", service_get_primary },
{ }
};
@@ -1584,12 +1569,6 @@
static void create_services(struct btd_gatt_client *client)
{
- /* Don't attempt to create any objects if experimental is disabled */
- if (!(g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL)) {
- info("GATT service objects disabled");
- return;
- }
-
DBG("Exporting objects for GATT services: %s", client->devaddr);
gatt_db_foreach_service(client->db, NULL, export_service, client);
diff --git a/src/gatt-database.c b/src/gatt-database.c
index bf1925b..0877b25 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -149,6 +149,10 @@
struct queue *ccc_states;
};
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+ void *user_data);
+typedef void (*btd_gatt_database_destroy_t) (void *data);
+
struct ccc_state {
uint16_t handle;
uint8_t value[2];
@@ -827,28 +831,6 @@
return ccc;
}
-struct gatt_db_attribute *
-btd_gatt_database_add_ccc(struct btd_gatt_database *database,
- uint16_t service_handle,
- btd_gatt_database_ccc_write_t write_callback,
- void *user_data,
- btd_gatt_database_destroy_t destroy)
-{
- struct gatt_db_attribute *service;
-
- if (!database || !service_handle)
- return NULL;
-
- service = gatt_db_get_attribute(database->db, service_handle);
- if (!service) {
- error("No service exists with handle: 0x%04x", service_handle);
- return NULL;
- }
-
- return service_add_ccc(service, database, write_callback, user_data,
- destroy);
-}
-
static void populate_gatt_service(struct btd_gatt_database *database)
{
bt_uuid_t uuid;
@@ -897,6 +879,7 @@
struct notify *notify = user_data;
struct ccc_state *ccc;
struct btd_device *device;
+ struct bt_gatt_server *server;
ccc = find_ccc_state(device_state, notify->ccc_handle);
if (!ccc)
@@ -909,7 +892,14 @@
&device_state->bdaddr,
device_state->bdaddr_type);
if (!device)
+ goto remove;
+
+ server = btd_device_get_gatt_server(device);
+ if (!server) {
+ if (!device_is_paired(device, device_state->bdaddr_type))
+ goto remove;
return;
+ }
/*
* TODO: If the device is not connected but bonded, send the
@@ -917,19 +907,23 @@
*/
if (!notify->indicate) {
DBG("GATT server sending notification");
- bt_gatt_server_send_notification(
- btd_device_get_gatt_server(device),
+ bt_gatt_server_send_notification(server,
notify->handle, notify->value,
notify->len);
return;
}
DBG("GATT server sending indication");
- bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
- notify->handle,
- notify->value,
+ bt_gatt_server_send_indication(server, notify->handle, notify->value,
notify->len, conf_cb,
NULL, NULL);
+
+ return;
+
+remove:
+ /* Remove device state if device no longer exists or is not paired */
+ if (queue_remove(notify->database->device_states, device_state))
+ device_state_free(device_state);
}
static void send_notification_to_devices(struct btd_gatt_database *database,
@@ -2579,11 +2573,11 @@
}
static const GDBusMethodTable manager_methods[] = {
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication",
- GDBUS_ARGS({ "application", "o" },
- { "options", "a{sv}" }), NULL,
- manager_register_app) },
- { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication",
+ { GDBUS_ASYNC_METHOD("RegisterApplication",
+ GDBUS_ARGS({ "application", "o" },
+ { "options", "a{sv}" }),
+ NULL, manager_register_app) },
+ { GDBUS_ASYNC_METHOD("UnregisterApplication",
GDBUS_ARGS({ "application", "o" }),
NULL, manager_unregister_app) },
{ }
diff --git a/src/gatt-database.h b/src/gatt-database.h
index 163b601..0d9106b 100644
--- a/src/gatt-database.h
+++ b/src/gatt-database.h
@@ -23,14 +23,3 @@
void btd_gatt_database_destroy(struct btd_gatt_database *database);
struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
-
-typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
- void *user_data);
-typedef void (*btd_gatt_database_destroy_t) (void *data);
-
-struct gatt_db_attribute *
-btd_gatt_database_add_ccc(struct btd_gatt_database *database,
- uint16_t service_handle,
- btd_gatt_database_ccc_write_t write_callback,
- void *user_data,
- btd_gatt_database_destroy_t destroy);
diff --git a/src/hcid.h b/src/hcid.h
index 60e2b0a..0b785ee 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -35,6 +35,8 @@
uint16_t autoto;
uint32_t pairto;
uint32_t discovto;
+ uint8_t privacy;
+
gboolean reverse_sdp;
gboolean name_resolv;
gboolean debug_keys;
diff --git a/src/main.c b/src/main.c
index c85bf19..21ca830 100644
--- a/src/main.c
+++ b/src/main.c
@@ -89,6 +89,7 @@
"DebugKeys",
"ControllerMode",
"MultiProfile",
+ "Privacy",
};
GKeyFile *btd_get_main_conf(void)
@@ -255,6 +256,26 @@
main_opts.autoto = val;
}
+ str = g_key_file_get_string(config, "General", "Privacy", &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ main_opts.privacy = 0x00;
+ } else {
+ DBG("privacy=%s", str);
+
+ if (!strcmp(str, "device"))
+ main_opts.privacy = 0x01;
+ else if (!strcmp(str, "off"))
+ main_opts.privacy = 0x00;
+ else {
+ DBG("Invalid privacy option: %s", str);
+ main_opts.privacy = 0x00;
+ }
+
+ g_free(str);
+ }
+
str = g_key_file_get_string(config, "General", "Name", &err);
if (err) {
DBG("%s", err->message);
diff --git a/src/main.conf b/src/main.conf
index 372fd8c..d9cd9e0 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -64,6 +64,13 @@
# 'false'.
#FastConnectable = false
+# Default privacy setting.
+# Enables use of private address.
+# Possible values: "off", "device", "network"
+# "network" option not supported currently
+# Defaults to "off"
+# Privacy = off
+
#[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
@@ -71,7 +78,7 @@
# timeout). The policy plugin should contain a sane set of values by
# default, but this list can be overridden here. By setting the list to
# empty the reconnection feature gets disabled.
-#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb, 0000111f-0000-1000-8000-00805f9b34fb, 0000110a-0000-1000-8000-00805f9b34fb
+#ReconnectUUIDs=00001112-0000-1000-8000-00805f9b34fb,0000111f-0000-1000-8000-00805f9b34fb,0000110a-0000-1000-8000-00805f9b34fb
# ReconnectAttempts define the number of attempts to reconnect after a link
# lost. Setting the value to 0 disables reconnecting feature.
@@ -81,7 +88,7 @@
# attempts.
# If the number of attempts defined in ReconnectAttempts is bigger than the
# set of intervals the last interval is repeated until the last attempt.
-#ReconnectIntervals=1, 2, 4, 8, 16, 32, 64
+#ReconnectIntervals=1,2,4,8,16,32,64
# AutoEnable defines option to enable all controllers when they are found.
# This includes adapters present on start as well as adapters that are plugged
diff --git a/src/profile.c b/src/profile.c
index c81a9f9..7c5318c 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -1047,7 +1047,7 @@
conn);
}
- if (conn->service && service_accept(conn->service) < 0)
+ if (conn->service && service_set_connecting(conn->service) < 0)
goto drop;
if (send_new_connection(ext, conn))
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
index c863508..54de393 100644
--- a/src/sdpd-server.c
+++ b/src/sdpd-server.c
@@ -164,7 +164,7 @@
}
len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
- if (len != sizeof(sdp_pdu_hdr_t)) {
+ if (len < 0 || (unsigned int) len < sizeof(sdp_pdu_hdr_t)) {
sdp_svcdb_collect_all(sk);
return FALSE;
}
diff --git a/src/service.c b/src/service.c
index 0da14ab..207ffae 100644
--- a/src/service.c
+++ b/src/service.c
@@ -197,7 +197,7 @@
}
if (!service->profile->accept)
- goto done;
+ return -ENOSYS;
err = service->profile->accept(service);
if (!err)
@@ -209,7 +209,27 @@
return err;
done:
+ if (service->state == BTD_SERVICE_STATE_DISCONNECTED)
+ change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+ return 0;
+}
+
+int service_set_connecting(struct btd_service *service)
+{
+ switch (service->state) {
+ case BTD_SERVICE_STATE_UNAVAILABLE:
+ return -EINVAL;
+ case BTD_SERVICE_STATE_DISCONNECTED:
+ break;
+ case BTD_SERVICE_STATE_CONNECTING:
+ case BTD_SERVICE_STATE_CONNECTED:
+ return 0;
+ case BTD_SERVICE_STATE_DISCONNECTING:
+ return -EBUSY;
+ }
+
change_state(service, BTD_SERVICE_STATE_CONNECTING, 0);
+
return 0;
}
diff --git a/src/service.h b/src/service.h
index c1f97f6..6f1edfb 100644
--- a/src/service.h
+++ b/src/service.h
@@ -49,6 +49,7 @@
void service_remove(struct btd_service *service);
int service_accept(struct btd_service *service);
+int service_set_connecting(struct btd_service *service);
/* Connection control API */
int btd_service_connect(struct btd_service *service);
diff --git a/src/shared/att.c b/src/shared/att.c
index f1e0f59..3071b51 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -603,13 +603,20 @@
security = bt_att_get_security(att);
if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION &&
- security < BT_ATT_SECURITY_MEDIUM)
+ security < BT_ATT_SECURITY_MEDIUM) {
security = BT_ATT_SECURITY_MEDIUM;
- else if (ecode == BT_ATT_ERROR_AUTHENTICATION &&
- security < BT_ATT_SECURITY_HIGH)
- security = BT_ATT_SECURITY_HIGH;
- else
+ } else if (ecode == BT_ATT_ERROR_AUTHENTICATION) {
+ if (security < BT_ATT_SECURITY_MEDIUM)
+ security = BT_ATT_SECURITY_MEDIUM;
+ else if (security < BT_ATT_SECURITY_HIGH)
+ security = BT_ATT_SECURITY_HIGH;
+ else if (security < BT_ATT_SECURITY_FIPS)
+ security = BT_ATT_SECURITY_FIPS;
+ else
+ return false;
+ } else {
return false;
+ }
return bt_att_set_security(att, security);
}
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index dab2b2f..3df8998 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -49,6 +49,10 @@
#define BTSNOOP_OPCODE_VENDOR_DIAG 11
#define BTSNOOP_OPCODE_SYSTEM_NOTE 12
#define BTSNOOP_OPCODE_USER_LOGGING 13
+#define BTSNOOP_OPCODE_CTRL_OPEN 14
+#define BTSNOOP_OPCODE_CTRL_CLOSE 15
+#define BTSNOOP_OPCODE_CTRL_COMMAND 16
+#define BTSNOOP_OPCODE_CTRL_EVENT 17
#define BTSNOOP_MAX_PACKET_SIZE (1486 + 4)
@@ -64,6 +68,7 @@
#define BTSNOOP_BUS_SDIO 6
#define BTSNOOP_BUS_SPI 7
#define BTSNOOP_BUS_I2C 8
+#define BTSNOOP_BUS_SMD 9
struct btsnoop_opcode_new_index {
uint8_t type;
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 8fd8a45..4386692 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -631,9 +631,11 @@
return false;
handle = gatt_db_attribute_get_handle(attr);
- bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
+
+ if (!bt_gatt_client_read_value(client, handle, ext_prop_read_cb,
discovery_op_ref(op),
- discovery_op_unref);
+ discovery_op_unref))
+ return false;
return true;
}
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index a0a5b26..6b39bb1 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -797,7 +797,7 @@
goto done;
}
- success = false;
+ success = true;
done:
discovery_op_complete(op, success, att_ecode);
diff --git a/src/shared/util.h b/src/shared/util.h
index ff705d0..7d28ae9 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -108,6 +108,11 @@
uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
void util_clear_uid(unsigned int *bitmap, uint8_t id);
+static inline int8_t get_s8(const void *ptr)
+{
+ return *((int8_t *) ptr);
+}
+
static inline uint8_t get_u8(const void *ptr)
{
return *((uint8_t *) ptr);
diff --git a/test/example-advertisement b/test/example-advertisement
index ce6e40f..7da0c7b 100755
--- a/test/example-advertisement
+++ b/test/example-advertisement
@@ -65,10 +65,10 @@
signature='s')
if self.manufacturer_data is not None:
properties['ManufacturerData'] = dbus.Dictionary(
- self.manufacturer_data, signature='qay')
+ self.manufacturer_data, signature='qv')
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
- signature='say')
+ signature='sv')
if self.include_tx_power is not None:
properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
return {LE_ADVERTISEMENT_IFACE: properties}
@@ -88,13 +88,13 @@
def add_manufacturer_data(self, manuf_code, data):
if not self.manufacturer_data:
- self.manufacturer_data = dict()
- self.manufacturer_data[manuf_code] = data
+ self.manufacturer_data = dbus.Dictionary({}, signature='qv')
+ self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
def add_service_data(self, uuid, data):
if not self.service_data:
- self.service_data = dict()
- self.service_data[uuid] = data
+ self.service_data = dbus.Dictionary({}, signature='sv')
+ self.service_data[uuid] = dbus.Array(data, signature='y')
@dbus.service.method(DBUS_PROP_IFACE,
in_signature='s',
diff --git a/test/example-gatt-client b/test/example-gatt-client
index 4d1df2f..b4bbaa9 100755
--- a/test/example-gatt-client
+++ b/test/example-gatt-client
@@ -193,14 +193,17 @@
om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE)
om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
+ print('Getting objects...')
objects = om.GetManagedObjects()
chrcs = []
+ # List characteristics found
for path, interfaces in objects.items():
if GATT_CHRC_IFACE not in interfaces.keys():
continue
chrcs.append(path)
+ # List sevices found
for path, interfaces in objects.items():
if GATT_SERVICE_IFACE not in interfaces.keys():
continue
diff --git a/test/example-gatt-server b/test/example-gatt-server
index 84905f3..24aaff9 100755
--- a/test/example-gatt-server
+++ b/test/example-gatt-server
@@ -42,6 +42,9 @@
class Application(dbus.service.Object):
+ """
+ org.bluez.GattApplication1 interface implementation
+ """
def __init__(self, bus):
self.path = '/'
self.services = []
@@ -74,6 +77,9 @@
class Service(dbus.service.Object):
+ """
+ org.bluez.GattService1 interface implementation
+ """
PATH_BASE = '/org/bluez/example/service'
def __init__(self, bus, index, uuid, primary):
@@ -121,6 +127,9 @@
class Characteristic(dbus.service.Object):
+ """
+ org.bluez.GattCharacteristic1 interface implementation
+ """
def __init__(self, bus, index, uuid, flags, service):
self.path = service.path + '/char' + str(index)
self.bus = bus
@@ -195,6 +204,9 @@
class Descriptor(dbus.service.Object):
+ """
+ org.bluez.GattDescriptor1 interface implementation
+ """
def __init__(self, bus, index, uuid, flags, characteristic):
self.path = characteristic.path + '/desc' + str(index)
self.bus = bus
@@ -222,7 +234,7 @@
if interface != GATT_DESC_IFACE:
raise InvalidArgsException()
- return self.get_properties()[GATT_CHRC_IFACE]
+ return self.get_properties()[GATT_DESC_IFACE]
@dbus.service.method(GATT_DESC_IFACE,
in_signature='a{sv}',
@@ -426,7 +438,7 @@
TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
def __init__(self, bus, index):
- Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+ Service.__init__(self, bus, index, self.TEST_SVC_UUID, True)
self.add_characteristic(TestCharacteristic(bus, 0, self))
self.add_characteristic(TestEncryptCharacteristic(bus, 1, self))
self.add_characteristic(TestSecureCharacteristic(bus, 2, self))
@@ -523,11 +535,11 @@
CharacteristicUserDescriptionDescriptor(bus, 3, self))
def ReadValue(self, options):
- print('TestCharacteristic Read: ' + repr(self.value))
+ print('TestEncryptCharacteristic Read: ' + repr(self.value))
return self.value
def WriteValue(self, value, options):
- print('TestCharacteristic Write: ' + repr(value))
+ print('TestEncryptCharacteristic Write: ' + repr(value))
self.value = value
class TestEncryptDescriptor(Descriptor):
@@ -564,16 +576,16 @@
['secure-read', 'secure-write'],
service)
self.value = []
- self.add_descriptor(TestEncryptDescriptor(bus, 2, self))
+ self.add_descriptor(TestSecureDescriptor(bus, 2, self))
self.add_descriptor(
CharacteristicUserDescriptionDescriptor(bus, 3, self))
def ReadValue(self, options):
- print('TestCharacteristic Read: ' + repr(self.value))
+ print('TestSecureCharacteristic Read: ' + repr(self.value))
return self.value
def WriteValue(self, value, options):
- print('TestCharacteristic Write: ' + repr(value))
+ print('TestSecureCharacteristic Write: ' + repr(value))
self.value = value
@@ -636,6 +648,8 @@
mainloop = GObject.MainLoop()
+ print('Registering GATT application...')
+
service_manager.RegisterApplication(app.get_path(), {},
reply_handler=register_app_cb,
error_handler=register_app_error_cb)
diff --git a/tools/btattach.c b/tools/btattach.c
index ec63ec9..9d1868a 100644
--- a/tools/btattach.c
+++ b/tools/btattach.c
@@ -187,7 +187,7 @@
"Usage:\n");
printf("\tbtattach [options]\n");
printf("options:\n"
- "\t-B, --bredr <device> Attach BR/EDR controller\n"
+ "\t-B, --bredr <device> Attach Primary controller\n"
"\t-A, --amp <device> Attach AMP controller\n"
"\t-P, --protocol <proto> Specify protocol type\n"
"\t-S, --speed <baudrate> Specify which baudrate to use\n"
@@ -217,6 +217,9 @@
{ "intel", HCI_UART_INTEL },
{ "bcm", HCI_UART_BCM },
{ "qca", HCI_UART_QCA },
+ { "ag6xx", HCI_UART_AG6XX },
+ { "nokia", HCI_UART_NOKIA },
+ { "mrvl", HCI_UART_MRVL },
{ }
};
@@ -300,7 +303,7 @@
unsigned long flags;
int fd;
- printf("Attaching BR/EDR controller to %s\n", bredr_path);
+ printf("Attaching Primary controller to %s\n", bredr_path);
flags = (1 << HCI_UART_RESET_ON_INIT);
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 4c86dcb..d5facc5 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -223,18 +223,14 @@
}
}
-static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
+static bool load_identity(const char *path, struct mgmt_irk_info *irk)
{
- char identity_path[PATH_MAX];
char *addr, *key;
unsigned int type;
int n;
FILE *fp;
- snprintf(identity_path, sizeof(identity_path),
- "/sys/kernel/debug/bluetooth/hci%u/identity", index);
-
- fp = fopen(identity_path, "r");
+ fp = fopen(path, "r");
if (!fp) {
error("Failed to open identity file: %s", strerror(errno));
return false;
@@ -1394,6 +1390,56 @@
noninteractive_quit(EXIT_SUCCESS);
}
+static void ext_info_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_ext_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_settings, current_settings;
+ char addr[18];
+
+ if (status != 0) {
+ error("Reading hci%u info failed with status 0x%02x (%s)",
+ index, status, mgmt_errstr(status));
+ goto done;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small info reply (%u bytes)", len);
+ goto done;
+ }
+
+ print("hci%u:\tPrimary controller", index);
+
+ ba2str(&rp->bdaddr, addr);
+ print("\taddr %s version %u manufacturer %u",
+ addr, rp->version, le16_to_cpu(rp->manufacturer));
+
+ supported_settings = le32_to_cpu(rp->supported_settings);
+ print("\tsupported settings: %s", settings2str(supported_settings));
+
+ current_settings = le32_to_cpu(rp->current_settings);
+ print("\tcurrent settings: %s", settings2str(current_settings));
+
+ if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_options_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ goto done;
+ }
+ return;
+ }
+
+done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
static void index_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1498,10 +1544,10 @@
switch (rp->entry[i].type) {
case 0x00:
print("Primary controller (hci%u,%s)", index, busstr);
- if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
- index, 0, NULL, info_rsp,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INFO,
+ index, 0, NULL, ext_info_rsp,
UINT_TO_PTR(index), NULL)) {
- error("Unable to send read_info cmd");
+ error("Unable to send read_ext_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
pending_index++;
@@ -2762,6 +2808,7 @@
static struct option irks_options[] = {
{ "help", 0, 0, 'h' },
{ "local", 1, 0, 'l' },
+ { "file", 1, 0, 'f' },
{ 0, 0, 0, 0 }
};
@@ -2772,6 +2819,7 @@
struct mgmt_cp_load_irks *cp;
uint8_t buf[sizeof(*cp) + 23 * MAX_IRKS];
uint16_t count, local_index;
+ char path[PATH_MAX];
int opt;
if (index == MGMT_INDEX_NONE)
@@ -2780,7 +2828,7 @@
cp = (void *) buf;
count = 0;
- while ((opt = getopt_long(argc, argv, "+l:h",
+ while ((opt = getopt_long(argc, argv, "+l:f:h",
irks_options, NULL)) != -1) {
switch (opt) {
case 'l':
@@ -2794,13 +2842,29 @@
local_index = atoi(optarg + 3);
else
local_index = atoi(optarg);
- if (!load_identity(local_index, &cp->irks[count])) {
+ snprintf(path, sizeof(path),
+ "/sys/kernel/debug/bluetooth/hci%u/identity",
+ local_index);
+ if (!load_identity(path, &cp->irks[count])) {
error("Unable to load identity");
optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
count++;
break;
+ case 'f':
+ if (count >= MAX_IRKS) {
+ error("Number of IRKs exceeded");
+ optind = 0;
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ if (!load_identity(optarg, &cp->irks[count])) {
+ error("Unable to load identities");
+ optind = 0;
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ count++;
+ break;
case 'h':
irks_usage();
optind = 0;
@@ -3904,7 +3968,9 @@
"\t -g, --general-discov \"general-discoverable\" flag\n"
"\t -l, --limited-discov \"limited-discoverable\" flag\n"
"\t -m, --managed-flags \"managed-flags\" flag\n"
- "\t -p, --tx-power \"tx-power\" flag");
+ "\t -p, --tx-power \"tx-power\" flag\n"\
+ "\t -a, --appearance \"appearance\" flag\n"\
+ "\t -n, --local-name \"local-name\" flag");
}
static struct option advsize_options[] = {
@@ -3914,6 +3980,8 @@
{ "limited-discov", 0, 0, 'l' },
{ "managed-flags", 0, 0, 'm' },
{ "tx-power", 0, 0, 'p' },
+ { "appearance", 0, 0, 'a' },
+ { "local-name", 0, 0, 'n' },
{ 0, 0, 0, 0}
};
@@ -3925,7 +3993,7 @@
uint32_t flags = 0;
int opt;
- while ((opt = getopt_long(argc, argv, "+cglmph",
+ while ((opt = getopt_long(argc, argv, "+cglmphna",
advsize_options, NULL)) != -1) {
switch (opt) {
case 'c':
@@ -3943,6 +4011,12 @@
case 'p':
flags |= MGMT_ADV_FLAG_TX_POWER;
break;
+ case 'a':
+ flags |= MGMT_ADV_FLAG_APPEARANCE;
+ break;
+ case 'n':
+ flags |= MGMT_ADV_FLAG_LOCAL_NAME;
+ break;
default:
advsize_usage();
return noninteractive_quit(EXIT_FAILURE);
@@ -4007,6 +4081,8 @@
"\t -c, --connectable \"connectable\" flag\n"
"\t -g, --general-discov \"general-discoverable\" flag\n"
"\t -l, --limited-discov \"limited-discoverable\" flag\n"
+ "\t -n, --scan-rsp-local-name \"local-name\" flag\n"
+ "\t -a, --scan-rsp-appearance \"appearance\" flag\n"
"\t -m, --managed-flags \"managed-flags\" flag\n"
"\t -p, --tx-power \"tx-power\" flag\n"
"e.g.:\n"
@@ -4088,7 +4164,7 @@
bool quit = true;
uint32_t flags = 0;
- while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:cglmph",
+ while ((opt = getopt_long(argc, argv, "+u:d:s:t:D:cglmphna",
add_adv_options, NULL)) != -1) {
switch (opt) {
case 'u':
@@ -4167,6 +4243,12 @@
case 'p':
flags |= MGMT_ADV_FLAG_TX_POWER;
break;
+ case 'n':
+ flags |= MGMT_ADV_FLAG_LOCAL_NAME;
+ break;
+ case 'a':
+ flags |= MGMT_ADV_FLAG_APPEARANCE;
+ break;
case 'h':
success = true;
default:
@@ -4290,6 +4372,40 @@
cmd_rm_adv(mgmt, index, 2, rm_argv);
}
+static void appearance_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0)
+ error("Could not set Appearance with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ else
+ print("Appearance successfully set");
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_appearance(struct mgmt *mgmt, uint16_t index, int argc,
+ char **argv)
+{
+ struct mgmt_cp_set_appearance cp;
+
+ if (argc < 2) {
+ print("Usage: appearance <appearance>");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.appearance = cpu_to_le16(strtol(argv[1], NULL, 0));
+
+ if (mgmt_send(mgmt, MGMT_OP_SET_APPEARANCE, index, sizeof(cp), &cp,
+ appearance_rsp, NULL, NULL) == 0) {
+ error("Unable to send appearance cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
struct cmd_info {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -4358,6 +4474,7 @@
{ "add-adv", cmd_add_adv, "Add advertising instance" },
{ "rm-adv", cmd_rm_adv, "Remove advertising instance" },
{ "clr-adv", cmd_clr_adv, "Clear advertising instances" },
+ { "appearance", cmd_appearance, "Set appearance" },
};
static void cmd_quit(struct mgmt *mgmt, uint16_t index,
diff --git a/tools/csr.c b/tools/csr.c
index 2c09189..15ae7c4 100644
--- a/tools/csr.c
+++ b/tools/csr.c
@@ -2756,7 +2756,7 @@
off++;
- while (1) {
+ while (length <= sizeof(array) - 2) {
value = strtol(off, &end, 16);
if (value == 0 && off == end)
break;
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 4279a33..249aab4 100644
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -42,6 +42,9 @@
#define HCI_UART_INTEL 6
#define HCI_UART_BCM 7
#define HCI_UART_QCA 8
+#define HCI_UART_AG6XX 9
+#define HCI_UART_NOKIA 10
+#define HCI_UART_MRVL 11
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
diff --git a/tools/l2test.c b/tools/l2test.c
index d4e3ae6..1819423 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -830,7 +830,7 @@
return;
}
- syslog(LOG_INFO, "Recevied %d bytes", len);
+ syslog(LOG_INFO, "Received %d bytes", len);
hexdump(buf, len);
}
}
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index b6f4873..d87d800 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -63,6 +63,7 @@
struct hciemu *hciemu;
enum hciemu_type hciemu_type;
int unmet_conditions;
+ int unmet_setup_conditions;
};
static void mgmt_debug(const char *str, void *user_data)
@@ -287,6 +288,27 @@
tester_print("Test condition added, total %d", data->unmet_conditions);
}
+static void test_add_setup_condition(struct test_data *data)
+{
+ data->unmet_setup_conditions++;
+
+ tester_print("Test setup condition added, total %d",
+ data->unmet_setup_conditions);
+}
+
+static void test_setup_condition_complete(struct test_data *data)
+{
+ data->unmet_setup_conditions--;
+
+ tester_print("Test setup condition complete, %d left",
+ data->unmet_setup_conditions);
+
+ if (data->unmet_setup_conditions > 0)
+ return;
+
+ tester_setup_complete();
+}
+
static void test_condition_complete(struct test_data *data)
{
data->unmet_conditions--;
@@ -384,6 +406,12 @@
tester_test_passed();
}
+struct setup_mgmt_cmd {
+ uint8_t send_opcode;
+ const void *send_param;
+ uint16_t send_len;
+};
+
struct generic_data {
const uint16_t *setup_settings;
bool setup_nobredr;
@@ -394,6 +422,7 @@
uint16_t setup_send_opcode;
const void *setup_send_param;
uint16_t setup_send_len;
+ const struct setup_mgmt_cmd *setup_mgmt_cmd_arr;
bool send_index_none;
uint16_t send_opcode;
const void *send_param;
@@ -1679,6 +1708,112 @@
.expect_status = MGMT_STATUS_REJECTED,
};
+static const uint8_t set_adv_set_appearance_param[2] = { 0x54, 0x65 };
+
+static const uint8_t set_adv_scan_rsp_data_appear_1[] = {
+ 0x04, /* Scan rsp data len */
+ 0x03, /* Local name data len */
+ 0x19, /* Complete name */
+ 0x54, 0x65,
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data set_adv_on_appearance_test_1 = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+ .setup_send_param = set_adv_set_appearance_param,
+ .setup_send_len = sizeof(set_adv_set_appearance_param),
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_adv_scan_rsp_data_appear_1,
+ .expect_hci_len = sizeof(set_adv_scan_rsp_data_appear_1),
+};
+
+static const char set_adv_set_local_name_param[260] = { 'T', 'e', 's', 't', ' ',
+ 'n', 'a', 'm', 'e' };
+
+static const uint8_t set_adv_scan_rsp_data_name_1[] = {
+ 0x0c, /* Scan rsp data len */
+ 0x0b, /* Local name data len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "Test name" */
+ 0x00, /* null */
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data set_adv_on_local_name_test_1 = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = set_adv_set_local_name_param,
+ .setup_send_len = sizeof(set_adv_set_local_name_param),
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_adv_scan_rsp_data_name_1,
+ .expect_hci_len = sizeof(set_adv_scan_rsp_data_name_1),
+};
+
+static const struct setup_mgmt_cmd set_advertising_mgmt_cmd_arr[] = {
+ {
+ .send_opcode = MGMT_OP_SET_APPEARANCE,
+ .send_param = set_adv_set_appearance_param,
+ .send_len = sizeof(set_adv_set_appearance_param),
+ },
+ {
+ .send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .send_param = set_adv_set_local_name_param,
+ .send_len = sizeof(set_adv_set_local_name_param),
+ },
+ { /* last element should always have opcode 0x00 */
+ .send_opcode = 0x00,
+ .send_param = NULL,
+ .send_len = 0,
+ }
+};
+
+static const uint8_t set_adv_scan_rsp_data_name_and_appearance[] = {
+ 0x10, /* scan rsp data len */
+ 0x03, /* appearance data len */
+ 0x19, /* eir_appearance */
+ 0x54, 0x65, /* appearance value */
+ 0x0b, /* local name data len */
+ 0x09, /* complete name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "test name" */
+ 0x00, /* null */
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+static const struct generic_data set_adv_on_local_name_appear_test_1 = {
+ .setup_settings = settings_powered_le,
+ .setup_mgmt_cmd_arr = set_advertising_mgmt_cmd_arr,
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_adv_scan_rsp_data_name_and_appearance,
+ .expect_hci_len = sizeof(set_adv_scan_rsp_data_name_and_appearance),
+};
+
static const char set_bredr_off_param[] = { 0x00 };
static const char set_bredr_on_param[] = { 0x01 };
static const char set_bredr_invalid_param[] = { 0x02 };
@@ -1760,6 +1895,20 @@
0x0a, 0x09, 'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e',
0x02, 0x0a, 0x00, };
+static const struct mgmt_cp_set_local_name set_local_name_cp = {
+ .name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e'},
+ .short_name = {'T', 'e', 's', 't'},
+};
+
+static const struct mgmt_cp_set_local_name set_local_name_longer_cp = {
+ .name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', '1', '2', '3'},
+};
+
+static const struct mgmt_cp_set_local_name set_local_name_long_short_cp = {
+ .name = {'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e', '1', '2', '3'},
+ .short_name = {'T', 'e', 's', 't'},
+};
+
static const struct generic_data set_local_name_test_1 = {
.send_opcode = MGMT_OP_SET_LOCAL_NAME,
.send_param = set_local_name_param,
@@ -3998,7 +4147,7 @@
};
static const uint8_t read_adv_features_rsp_1[] = {
- 0x1f, 0x00, 0x00, 0x00, /* supported flags */
+ 0x7f, 0x00, 0x00, 0x00, /* supported flags */
0x1f, /* max_adv_data_len */
0x1f, /* max_scan_rsp_len */
0x05, /* max_instances */
@@ -4013,7 +4162,7 @@
};
static const uint8_t read_adv_features_rsp_2[] = {
- 0x1f, 0x00, 0x00, 0x00, /* supported flags */
+ 0x7f, 0x00, 0x00, 0x00, /* supported flags */
0x1f, /* max_adv_data_len */
0x1f, /* max_scan_rsp_len */
0x05, /* max_instances */
@@ -4862,6 +5011,210 @@
.expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
};
+static const char ext_ctrl_info1[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+ 0x08, /* version */
+ 0x3f, 0x00, /* manufacturer */
+ 0xff, 0xbf, 0x00, 0x00, /* supported settings */
+ 0x80, 0x00, 0x00, 0x00, /* current settings */
+ 0x09, 0x00, /* eir length */
+ 0x04, /* dev class length */
+ 0x0d, /* dev class info */
+ 0x00, /* minor */
+ 0x00, /* major */
+ 0x00, /* service classes */
+ 0x01, /* complete name data length */
+ 0x09, /* complete name flag */
+ 0x01, /* short name data length */
+ 0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info1 = {
+ .send_opcode = MGMT_OP_READ_EXT_INFO,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = ext_ctrl_info1,
+ .expect_len = sizeof(ext_ctrl_info1),
+};
+
+static const char set_dev_class1[] = { 0x03, 0xe0 };
+
+static const struct setup_mgmt_cmd set_dev_class_cmd_arr1[] = {
+ {
+ .send_opcode = MGMT_OP_SET_DEV_CLASS,
+ .send_param = set_dev_class1,
+ .send_len = sizeof(set_dev_class1),
+ },
+ {
+ .send_opcode = MGMT_OP_ADD_UUID,
+ .send_param = add_spp_uuid_param,
+ .send_len = sizeof(add_spp_uuid_param),
+ },
+ { /* last element should always have opcode 0x00 */
+ .send_opcode = 0x00,
+ .send_param = NULL,
+ .send_len = 0,
+ }
+};
+
+static const char ext_ctrl_info2[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+ 0x08, /* version */
+ 0x3f, 0x00, /* manufacturer */
+ 0xff, 0xbf, 0x00, 0x00, /* supported settings */
+ 0x81, 0x02, 0x00, 0x00, /* current settings */
+ 0x0D, 0x00, /* eir length */
+ 0x04, /* dev class length */
+ 0x0d, /* dev class info */
+ 0xe0, /* minor */
+ 0x03, /* major */
+ 0x00, /* service classes */
+ 0x03, /* appearance length */
+ 0x19, /* EIR_APPEARANCE */
+ 0x00, /* Appearance value */
+ 0x00,
+ 0x01, /* complete name data length */
+ 0x09, /* complete name flag */
+ 0x01, /* short name data length */
+ 0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info2 = {
+ .setup_settings = settings_powered_le,
+ .setup_mgmt_cmd_arr = set_dev_class_cmd_arr1,
+ .send_opcode = MGMT_OP_READ_EXT_INFO,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = ext_ctrl_info2,
+ .expect_len = sizeof(ext_ctrl_info2),
+};
+
+static const char ext_ctrl_info3[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+ 0x08, /* version */
+ 0x3f, 0x00, /* manufacturer */
+ 0xff, 0xbf, 0x00, 0x00, /* supported settings */
+ 0x80, 0x02, 0x00, 0x00, /* current settings */
+ 0x16, 0x00, /* eir length */
+ 0x04, /* dev class length */
+ 0x0d, /* dev class info */
+ 0x00, /* minor */
+ 0x00, /* major */
+ 0x00, /* service classes */
+ 0x03, /* appearance length */
+ 0x19, /* EIR_APPEARANCE */
+ 0x00, /* Appearance value */
+ 0x00,
+ 0x0A, /* Local name length */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+ 0x01, /* short name data length */
+ 0x08, /* short name flag */
+};
+
+static const struct generic_data read_ext_ctrl_info3 = {
+ .setup_settings = settings_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = set_local_name_param,
+ .setup_send_len = sizeof(set_local_name_param),
+ .send_opcode = MGMT_OP_READ_EXT_INFO,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = ext_ctrl_info3,
+ .expect_len = sizeof(ext_ctrl_info3),
+};
+
+static const char ext_ctrl_info4[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+ 0x08, /* version */
+ 0x3f, 0x00, /* manufacturer */
+ 0xff, 0xbf, 0x00, 0x00, /* supported settings */
+ 0x80, 0x02, 0x00, 0x00, /* current settings */
+ 0x1a, 0x00, /* eir length */
+ 0x04, /* dev class length */
+ 0x0d, /* dev class info */
+ 0x00, /* minor */
+ 0x00, /* major */
+ 0x00, /* service classes */
+ 0x03, /* appearance length */
+ 0x19, /* EIR_APPEARANCE */
+ 0x00, /* Appearance value */
+ 0x00,
+ 0x0A, /* Complete Local name len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+ 0x05, /* Short Local name len */
+ 0x08, /* Short name */
+ 0x54, 0x65, 0x73, 0x74, /* "Test" */
+};
+
+static const struct generic_data read_ext_ctrl_info4 = {
+ .setup_settings = settings_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_cp,
+ .setup_send_len = sizeof(set_local_name_cp),
+ .send_opcode = MGMT_OP_READ_EXT_INFO,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = ext_ctrl_info4,
+ .expect_len = sizeof(ext_ctrl_info4),
+};
+
+static const struct setup_mgmt_cmd set_dev_class_cmd_arr2[] = {
+ {
+ .send_opcode = MGMT_OP_SET_DEV_CLASS,
+ .send_param = set_dev_class1,
+ .send_len = sizeof(set_dev_class1),
+ },
+ {
+ .send_opcode = MGMT_OP_ADD_UUID,
+ .send_param = add_spp_uuid_param,
+ .send_len = sizeof(add_spp_uuid_param),
+ },
+ {
+ .send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .send_param = &set_local_name_cp,
+ .send_len = sizeof(set_local_name_cp),
+ },
+ { /* last element should always have opcode 0x00 */
+ .send_opcode = 0x00,
+ .send_param = NULL,
+ .send_len = 0,
+ }
+};
+
+static const char ext_ctrl_info5[] = {
+ 0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
+ 0x08, /* version */
+ 0x3f, 0x00, /* manufacturer */
+ 0xff, 0xbf, 0x00, 0x00, /* supported settings */
+ 0x81, 0x02, 0x00, 0x00, /* current settings */
+ 0x1a, 0x00, /* eir len */
+ 0x04, /* dev class len */
+ 0x0d, /* dev class info */
+ 0xe0, /* minor */
+ 0x03, /* major */
+ 0x00, /* service classes */
+ 0x03, /* appearance length */
+ 0x19, /* EIR_APPEARANCE */
+ 0x00, /* Appearance value */
+ 0x00,
+ 0x0A, /* Complete Local name len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74,
+ 0x20, 0x6E, 0x61, 0x6D, 0x65, /* "Test name" */
+ 0x05, /* Short Local name len */
+ 0x08, /* Short name */
+ 0x54, 0x65, 0x73, 0x74, /* "Test" */
+};
+
+static const struct generic_data read_ext_ctrl_info5 = {
+ .setup_settings = settings_powered_le,
+ .setup_mgmt_cmd_arr = set_dev_class_cmd_arr2,
+ .send_opcode = MGMT_OP_READ_EXT_INFO,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = ext_ctrl_info5,
+ .expect_len = sizeof(ext_ctrl_info5),
+};
+
static void client_cmd_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
@@ -5844,6 +6197,35 @@
test_condition_complete(data);
}
+static void command_setup_hci_callback(uint16_t opcode, const void *param,
+ uint8_t length, void *user_data)
+{
+ struct test_data *data = user_data;
+ const struct generic_data *test = data->test_data;
+ const void *setup_expect_hci_param = test->setup_expect_hci_param;
+ uint8_t setup_expect_hci_len = test->setup_expect_hci_len;
+
+ tester_print("HCI Command 0x%04x length %u", opcode, length);
+
+ if (opcode != test->setup_expect_hci_command)
+ return;
+
+ if (length != setup_expect_hci_len) {
+ tester_warn("Invalid parameter size for HCI command");
+ tester_test_failed();
+ return;
+ }
+
+ if (memcmp(param, setup_expect_hci_param, length) != 0) {
+ tester_warn("Unexpected HCI command parameter value");
+ tester_test_failed();
+ return;
+ }
+
+ hciemu_clear_master_post_command_hooks(data->hciemu);
+ test_setup_condition_complete(data);
+}
+
static void command_hci_callback(uint16_t opcode, const void *param,
uint8_t length, void *user_data)
{
@@ -5875,6 +6257,484 @@
test_condition_complete(data);
}
+static void setup_mgmt_cmd_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+ test_setup_condition_complete(user_data);
+}
+
+static void setup_command_generic(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct generic_data *test = data->test_data;
+ const void *send_param = test->setup_send_param;
+ uint16_t send_len = test->setup_send_len;
+ size_t i = 0;
+
+ if (test->setup_expect_hci_command) {
+ tester_print("Registering setup expected HCI command callback");
+ tester_print("Setup expected HCI command 0x%04x",
+ test->setup_expect_hci_command);
+ hciemu_add_master_post_command_hook(data->hciemu,
+ command_setup_hci_callback, data);
+ test_add_setup_condition(data);
+ }
+
+ if (test->setup_send_opcode) {
+ tester_print("Setup sending %s (0x%04x)",
+ mgmt_opstr(test->setup_send_opcode),
+ test->setup_send_opcode);
+ mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index,
+ send_len, send_param,
+ setup_mgmt_cmd_callback,
+ data, NULL);
+ test_add_setup_condition(data);
+ return;
+ }
+
+ tester_print("Sending setup opcode array");
+ for (; test->setup_mgmt_cmd_arr + i; ++i) {
+ const struct setup_mgmt_cmd *cmd = test->setup_mgmt_cmd_arr + i;
+
+ if (cmd->send_opcode == 0x00)
+ break;
+
+ tester_print("Setup sending %s (0x%04x)",
+ mgmt_opstr(cmd->send_opcode),
+ cmd->send_opcode);
+
+ mgmt_send(data->mgmt, cmd->send_opcode, data->mgmt_index,
+ cmd->send_len, cmd->send_param,
+ setup_mgmt_cmd_callback,
+ data, NULL);
+ test_add_setup_condition(data);
+ }
+}
+
+static const uint8_t add_advertising_param_empty[] = {
+ 0x01, /* adv instance */
+ 0x00, 0x00, 0x00, 0x00, /* flags: none */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x00, /* scan rsp len */
+};
+
+static const struct generic_data add_advertising_empty_scrsp = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = set_local_name_param,
+ .setup_send_len = sizeof(set_local_name_param),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_empty,
+ .send_len = sizeof(add_advertising_param_empty),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_data_only_ok[] = {
+ 0x01, /* adv instance */
+ 0x00, 0x00, 0x00, 0x00, /* flags: none */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x1f, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_data_only_ok = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_scrsp_data_only_ok,
+ .send_len = sizeof(add_advertising_param_scrsp_data_only_ok),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_data_only_too_long[] = {
+ 0x01, /* adv instance */
+ 0x00, 0x00, 0x00, 0x00, /* flags: none */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x20, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_data_only_too_long = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_scrsp_data_only_too_long,
+ .send_len = sizeof(add_advertising_param_scrsp_data_only_too_long),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+ .expect_param = NULL,
+ .expect_len = 0,
+};
+
+static const uint8_t set_appearance_param[2] = { 0x54, 0x65 };
+
+static const uint8_t add_advertising_param_scrsp_appear_data_ok[] = {
+ 0x01, /* adv instance */
+ 0x20, 0x00, 0x00, 0x00, /* flags: appearance */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x1b, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_data_ok = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+ .setup_send_param = set_appearance_param,
+ .setup_send_len = sizeof(set_appearance_param),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_scrsp_appear_data_ok,
+ .send_len = sizeof(add_advertising_param_scrsp_appear_data_ok),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_param_scrsp_appear_data_too_long[] = {
+ 0x01, /* adv instance */
+ 0x20, 0x00, 0x00, 0x00, /* flags: appearance */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x1c, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_data_too_long = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_APPEARANCE,
+ .setup_send_param = set_appearance_param,
+ .setup_send_len = sizeof(set_appearance_param),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_scrsp_appear_data_too_long,
+ .send_len = sizeof(add_advertising_param_scrsp_appear_data_too_long),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+ .expect_param = NULL,
+ .expect_len = 0,
+};
+
+static const uint8_t add_advertising_param_scrsp_appear_null[] = {
+ 0x01, /* adv instance */
+ 0x20, 0x00, 0x00, 0x00, /* flags: appearance */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x01, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00,
+};
+
+static const struct generic_data add_advertising_scrsp_appear_null = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_scrsp_appear_null,
+ .send_len = sizeof(add_advertising_param_scrsp_appear_null),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+};
+
+static const uint8_t add_advertising_empty_param[] = {
+ 0x01, /* adv instance */
+ 0x40, 0x00, 0x00, 0x00, /* flags: local name*/
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x01, /* scan rsp len */
+ /* scan rsp data: */
+ 0x00,
+};
+
+static const uint8_t scan_rsp_data_empty[] = {
+ 0x01, /* scan rsp data len */
+ 0x00, /* scan rsp data */
+ /* placeholder data */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_no_name_set = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_empty_param,
+ .send_len = sizeof(add_advertising_empty_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = scan_rsp_data_empty,
+ .expect_hci_len = sizeof(scan_rsp_data_empty),
+};
+
+static const uint8_t add_advertising_param_name[] = {
+ 0x01, /* adv instance */
+ 0x40, 0x00, 0x00, 0x00, /* flags: Add local name to scan_rsp */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x00, /* scan rsp len */
+};
+
+static const uint8_t set_scan_rsp_data_name_fits_in_scrsp[] = {
+ 0x0c, /* Scan rsp data len */
+ 0x0b, /* Local name data len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, /* "Test name" */
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_name_fits_in_scrsp = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_cp,
+ .setup_send_len = sizeof(set_local_name_cp),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name,
+ .send_len = sizeof(add_advertising_param_name),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_data_name_fits_in_scrsp,
+ .expect_hci_len = sizeof(set_scan_rsp_data_name_fits_in_scrsp),
+};
+
+static const uint8_t set_scan_rsp_data_shortened_name_fits[] = {
+ 0x0d, /* Scan rsp data len */
+ 0x0c, /* Local name data len */
+ 0x08, /* Short name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x31,
+ /* "Test name1" */
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_shortened_name_in_scrsp = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_longer_cp,
+ .setup_send_len = sizeof(set_local_name_longer_cp),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name,
+ .send_len = sizeof(add_advertising_param_name),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_data_shortened_name_fits,
+ .expect_hci_len = sizeof(set_scan_rsp_data_shortened_name_fits),
+};
+
+static const uint8_t set_scan_rsp_data_short_name_fits[] = {
+ 0x07, /* Scan rsp data len */
+ 0x06, /* Local name data len */
+ 0x08, /* Short name */
+ 0x54, 0x65, 0x73, 0x74,
+ /* "Test*/
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_short_name_in_scrsp = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_long_short_cp,
+ .setup_send_len = sizeof(set_local_name_long_short_cp),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name,
+ .send_len = sizeof(add_advertising_param_name),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_data_short_name_fits,
+ .expect_hci_len = sizeof(set_scan_rsp_data_short_name_fits),
+};
+
+static const uint8_t add_advertising_param_name_data_ok[] = {
+ 0x01, /* adv instance */
+ 0x40, 0x00, 0x00, 0x00, /* flags: local name */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x12, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_data_param_name_data_ok[] = {
+ 0x1e, /* Scan rsp data len */
+ /* scan rsp data */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, /* Local name data len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x00,
+ /* "Test name" */
+ /* padding */
+ 0x00,
+};
+
+static const struct generic_data add_advertising_name_data_ok = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_cp,
+ .setup_send_len = sizeof(set_local_name_cp),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name_data_ok,
+ .send_len = sizeof(add_advertising_param_name_data_ok),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_data_param_name_data_ok,
+ .expect_hci_len = sizeof(set_scan_rsp_data_param_name_data_ok),
+};
+
+static const uint8_t add_advertising_param_name_data_inv[] = {
+ 0x01, /* adv instance */
+ 0x40, 0x00, 0x00, 0x00, /* flags: local name */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x14, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_name_data_inv = {
+ .setup_settings = settings_powered_le,
+ .setup_send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .setup_send_param = &set_local_name_cp,
+ .setup_send_len = sizeof(set_local_name_cp),
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name_data_inv,
+ .send_len = sizeof(add_advertising_param_name_data_inv),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+ .expect_param = NULL,
+ .expect_len = 0,
+};
+
+static const uint8_t add_advertising_param_name_data_appear[] = {
+ 0x01, /* adv instance */
+ 0x60, 0x00, 0x00, 0x00, /* flags: local name + appearance */
+ 0x00, 0x00, /* duration: default */
+ 0x00, 0x00, /* timeout: none */
+ 0x00, /* adv data len */
+ 0x0e, /* scan rsp len */
+ /* adv data: */
+ /* scan rsp data: */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+static const struct setup_mgmt_cmd add_advertising_mgmt_cmd_arr[] = {
+ {
+ .send_opcode = MGMT_OP_SET_APPEARANCE,
+ .send_param = set_appearance_param,
+ .send_len = sizeof(set_appearance_param),
+ },
+ {
+ .send_opcode = MGMT_OP_SET_LOCAL_NAME,
+ .send_param = &set_local_name_cp,
+ .send_len = sizeof(set_local_name_cp),
+ },
+ { /* last element should always have opcode 0x00 */
+ .send_opcode = 0x00,
+ .send_param = NULL,
+ .send_len = 0,
+ }
+};
+
+static const uint8_t set_scan_rsp_data_name_data_appear[] = {
+ 0x1e, /* Scan rsp data len */
+ 0x03, /* appearance len */
+ 0x19, /* EIR_APPEARANCE */
+ 0x54, 0x65, /* appearance value */
+ /* scan rsp data */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x0b, /* Local name data len */
+ 0x09, /* Complete name */
+ 0x54, 0x65, 0x73, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x00,
+ /* "Test name" */
+ /* padding */
+ 0x00,
+};
+
+static const struct generic_data add_advertising_name_data_appear = {
+ .setup_settings = settings_powered_le,
+ .setup_mgmt_cmd_arr = add_advertising_mgmt_cmd_arr,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_name_data_appear,
+ .send_len = sizeof(add_advertising_param_name_data_appear),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = advertising_instance1_param,
+ .expect_len = sizeof(advertising_instance1_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_data_name_data_appear,
+ .expect_hci_len = sizeof(set_scan_rsp_data_name_data_appear),
+};
+
+static const struct generic_data set_appearance_not_supported = {
+ .send_opcode = MGMT_OP_SET_APPEARANCE,
+ .send_param = set_appearance_param,
+ .send_len = sizeof(set_appearance_param),
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+ .expect_param = NULL,
+ .expect_len = 0,
+};
+
+static const struct generic_data set_appearance_success = {
+ .send_opcode = MGMT_OP_SET_APPEARANCE,
+ .send_param = set_appearance_param,
+ .send_len = sizeof(set_appearance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = NULL,
+ .expect_len = 0,
+};
+
static bool power_off(uint16_t index)
{
int sk, err;
@@ -6489,6 +7349,18 @@
&set_adv_on_rejected_test_1,
NULL, test_command_generic);
+ test_bredrle("Set Advertising on - Appearance 1",
+ &set_adv_on_appearance_test_1,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Set Advertising on - Local name 1",
+ &set_adv_on_local_name_test_1,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Set Advertising on - Name + Appear 1",
+ &set_adv_on_local_name_appear_test_1,
+ setup_command_generic, test_command_generic);
+
test_bredrle("Set BR/EDR off - Success 1",
&set_bredr_off_success_test_1,
NULL, test_command_generic);
@@ -7071,10 +7943,69 @@
setup_add_advertising,
test_command_generic);
+ test_bredrle("Add Advertising - Success (Empty ScRsp)",
+ &add_advertising_empty_scrsp,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Success (ScRsp only)",
+ &add_advertising_scrsp_data_only_ok,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Invalid Params (ScRsp too long)",
+ &add_advertising_scrsp_data_only_too_long,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Success (ScRsp appear)",
+ &add_advertising_scrsp_appear_data_ok,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Add Advertising - Invalid Params (ScRsp appear long)",
+ &add_advertising_scrsp_appear_data_too_long,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Appear is null)",
+ &add_advertising_scrsp_appear_null,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Name is null)",
+ &add_advertising_no_name_set,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Complete name)",
+ &add_advertising_name_fits_in_scrsp,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Shortened name)",
+ &add_advertising_shortened_name_in_scrsp,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Short name)",
+ &add_advertising_short_name_in_scrsp,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Name + data)",
+ &add_advertising_name_data_ok,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Invalid Params (Name + data)",
+ &add_advertising_name_data_inv,
+ setup_command_generic,
+ test_command_generic);
+
+ test_bredrle("Add Advertising - Success (Name+data+appear)",
+ &add_advertising_name_data_appear,
+ setup_command_generic,
+ test_command_generic);
test_bredrle("Remove Advertising - Invalid Params 1",
&remove_advertising_fail_1,
NULL, test_command_generic);
+
test_bredrle("Remove Advertising - Success 1",
&remove_advertising_success_1,
setup_add_advertising,
@@ -7100,6 +8031,41 @@
setup_add_advertising_duration,
test_command_generic, 3);
+ test_bredr("Set appearance - BR/EDR only",
+ &set_appearance_not_supported,
+ NULL,
+ test_command_generic);
+
+ test_bredrle("Set appearance - BR/EDR LE",
+ &set_appearance_success,
+ NULL,
+ test_command_generic);
+
+ test_le("Set appearance - LE only",
+ &set_appearance_success,
+ NULL,
+ test_command_generic);
+
+ test_bredrle("Read Ext Controller Info 1",
+ &read_ext_ctrl_info1,
+ NULL, test_command_generic);
+
+ test_bredrle("Read Ext Controller Info 2",
+ &read_ext_ctrl_info2,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Read Ext Controller Info 3",
+ &read_ext_ctrl_info3,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Read Ext Controller Info 4",
+ &read_ext_ctrl_info4,
+ setup_command_generic, test_command_generic);
+
+ test_bredrle("Read Ext Controller Info 5",
+ &read_ext_ctrl_info5,
+ setup_command_generic, test_command_generic);
+
test_bredrle("Read Local OOB Data - Not powered",
&read_local_oob_not_powered_test,
NULL, test_command_generic);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
index f755390..d5b2815 100755
--- a/tools/parse_companies.pl
+++ b/tools/parse_companies.pl
@@ -8,7 +8,11 @@
my %known_entities = (
'nbsp' => ' ',
+ 'aacute' => 'á',
'eacute' => 'é',
+ 'iacute' => 'í',
+ 'oacute' => 'ó',
+ 'uacute' => 'ú',
'auml' => 'ä',
'uuml' => 'ü',
'Uuml' => 'Ü',
@@ -23,8 +27,7 @@
}
foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
if ($entity ne 'amp') {
- print "Unable to convert &$entity;, giving up\n";
- exit 1;
+ die "\nparse_companies.pl: Unable to convert &$entity; giving up\n";
}
}
$name =~ s/&/&/ig;
@@ -51,6 +54,7 @@
my $name = uri_decode($1);
$name =~ s/^\s+//g; # kill leading
$name =~ s/\s+$//g; # and trailing space
+ $name =~ s/"/\\"/g; # escape double quotes
my $id = hex($identifier);
if ($id != 65535) {
print "\tcase $id:\n";
diff --git a/tools/scotest.c b/tools/scotest.c
index 596e403..f894c24 100644
--- a/tools/scotest.c
+++ b/tools/scotest.c
@@ -265,7 +265,7 @@
syslog(LOG_INFO,"Receiving ...");
while ((len = read(sk, buf, data_size)) > 0)
- syslog(LOG_INFO, "Recevied %d bytes", len);
+ syslog(LOG_INFO, "Received %d bytes", len);
}
static void recv_mode(int sk)
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index d24c9b2..90b091e 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -593,7 +593,7 @@
data->ra_type, data->ra, confirm))
return false;
- if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) {
+ if (memcmp(data->pcnf, confirm, sizeof(data->pcnf)) != 0) {
tester_warn("Confirmation values don't match");
return false;
}
diff --git a/tools/update_compids.sh b/tools/update_compids.sh
index 7c4cc12..e82d3e4 100755
--- a/tools/update_compids.sh
+++ b/tools/update_compids.sh
@@ -25,7 +25,7 @@
path=specifications/assigned-numbers/company-identifiers
# Use "iconv -c" to strip unwanted unicode characters
-curl https://www.bluetooth.com/$path | \
+curl --insecure https://www.bluetooth.com/$path | \
$scriptdir/tools/parse_companies.pl >> new.c
if ! grep -q "return \"" new.c; then
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 8129719..c7a8fa5 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -2414,6 +2414,14 @@
0x18),
raw_pdu(0x01, 0x06, 0x08, 0x00, 0x0a));
+ define_test_att("/TP/GAD/CL/BV-02-C-1-alternative",
+ test_search_primary, &uuid_16,
+ NULL,
+ MTU_EXCHANGE_CLIENT_PDUS,
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+ 0x18),
+ raw_pdu(0x07, 0x01, 0x00, 0xFF, 0xFF));
+
define_test_att("/TP/GAD/CL/BV-02-C-2", test_search_primary, &uuid_128,
NULL,
MTU_EXCHANGE_CLIENT_PDUS,