diff -Nru bluez-5.37/android/bluetoothd-snoop.c bluez-5.41/android/bluetoothd-snoop.c --- bluez-5.37/android/bluetoothd-snoop.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/android/bluetoothd-snoop.c 2016-05-26 16:51:11.000000000 +0000 @@ -139,7 +139,7 @@ flags = get_flags_from_opcode(opcode); if (flags != 0xff) - btsnoop_write(snoop, tv, flags, monitor_buf, pktlen); + btsnoop_write(snoop, tv, flags, 0, monitor_buf, pktlen); } } diff -Nru bluez-5.37/ChangeLog bluez-5.41/ChangeLog --- bluez-5.37/ChangeLog 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/ChangeLog 2016-07-17 21:13:15.000000000 +0000 @@ -1,3 +1,41 @@ +ver 5.41: + Fix issue with service state changes handling. + Fix issue with AVRCP and no available player. + Fix issue with handling discovery filters. + Fix issue with handling temporary addresses. + Fix issue with GATT MTU size and BR/EDR links. + Fix issue with OBEX and creating directories. + +ver 5.40: + Fix issue with not storing GATT attributes. + Fix issue with optional GATT notifications. + Fix issue with reading GATT extended properties. + Fix issue with GATT device name properties. + Fix issue with previously paired devices. + Fix issue with handling device removal. + Fix issue with profile connection handling. + Add support for TTY monitor protocol. + +ver 5.39: + Fix issue with missing uHID kernel support. + Fix issue with GATT reliable write handling. + Fix issue with GATT service changed handling. + Fix issue with GATT execute write handling. + Fix issue with AVRCP player event handling. + Fix issue with AVRCP controller handling. + Fix issue with AVDTP connection handling. + Fix issue with AVDTP error handling. + +ver 5.38: + Fix issue with stack overflow and UUID handling. + Fix issue with ObjectManager interface and GATT. + Fix issue with GATT database and error handling. + Fix issue with GATT client notifications. + Fix issue with GATT object ordering. + Fix issue with GATT default MTU exchange. + Fix issue with device attribute clearing. + Fix issue with AVRCP capabilities request. + ver 5.37: Fix issue with registering external profiles. Fix issue with connecting external profiles. diff -Nru bluez-5.37/client/gatt.c bluez-5.41/client/gatt.c --- bluez-5.37/client/gatt.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/client/gatt.c 2016-05-26 16:51:11.000000000 +0000 @@ -379,9 +379,23 @@ rl_hexdump(value, len); } +static void read_setup(DBusMessageIter *iter, void *user_data) +{ + DBusMessageIter dict; + + 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); + /* TODO: Add offset support */ + dbus_message_iter_close_container(iter, &dict); +} + static void read_attribute(GDBusProxy *proxy) { - if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply, + if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply, NULL, NULL) == FALSE) { rl_printf("Failed to read\n"); return; @@ -421,12 +435,21 @@ static void write_setup(DBusMessageIter *iter, void *user_data) { struct iovec *iov = user_data; - DBusMessageIter array; + DBusMessageIter array, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &iov->iov_base, iov->iov_len); dbus_message_iter_close_container(iter, &array); + + 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); + /* TODO: Add offset support */ + dbus_message_iter_close_container(iter, &dict); } static void write_attribute(GDBusProxy *proxy, char *arg) diff -Nru bluez-5.37/client/main.c bluez-5.41/client/main.c --- bluez-5.37/client/main.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/client/main.c 2016-07-17 21:13:15.000000000 +0000 @@ -50,7 +50,7 @@ #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF #define PROMPT_ON COLOR_BLUE "[bluetooth]" COLOR_OFF "# " -#define PROMPT_OFF "[bluetooth]# " +#define PROMPT_OFF "Waiting to connect to bluetoothd..." static GMainLoop *main_loop; static DBusConnection *dbus_conn; @@ -82,16 +82,56 @@ printf("Leaking proxy %p\n", data); } +static gboolean input_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + if (condition & G_IO_IN) { + rl_callback_read_char(); + return TRUE; + } + + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_main_loop_quit(main_loop); + return FALSE; + } + + return TRUE; +} + +static guint setup_standard_input(void) +{ + GIOChannel *channel; + guint source; + + channel = g_io_channel_unix_new(fileno(stdin)); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + input_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + static void connect_handler(DBusConnection *connection, void *user_data) { rl_set_prompt(PROMPT_ON); printf("\r"); rl_on_new_line(); rl_redisplay(); + + if (!input) + input = setup_standard_input(); } static void disconnect_handler(DBusConnection *connection, void *user_data) { + if (input > 0) { + g_source_remove(input); + input = 0; + } + rl_set_prompt(PROMPT_OFF); printf("\r"); rl_on_new_line(); @@ -321,6 +361,59 @@ return FALSE; } +static void set_default_device(GDBusProxy *proxy, const char *attribute) +{ + char *desc = NULL; + DBusMessageIter iter; + const char *path; + + default_dev = proxy; + + if (proxy == NULL) { + default_attr = NULL; + goto done; + } + + if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) { + if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) + goto done; + } + + path = g_dbus_proxy_get_path(proxy); + + dbus_message_iter_get_basic(&iter, &desc); + desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc, + attribute ? ":" : "", + attribute ? attribute + strlen(path) : ""); + +done: + rl_set_prompt(desc ? desc : PROMPT_ON); + printf("\r"); + rl_on_new_line(); + g_free(desc); +} + +static void device_added(GDBusProxy *proxy) +{ + DBusMessageIter iter; + + dev_list = g_list_append(dev_list, proxy); + + print_device(proxy, COLORED_NEW); + + if (default_dev) + return; + + if (g_dbus_proxy_get_property(proxy, "Connected", &iter)) { + dbus_bool_t connected; + + dbus_message_iter_get_basic(&iter, &connected); + + if (connected) + set_default_device(proxy, NULL); + } +} + static void proxy_added(GDBusProxy *proxy, void *user_data) { const char *interface; @@ -328,11 +421,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_append(dev_list, proxy); + if (device_is_child(proxy, default_ctrl) == TRUE) + device_added(proxy); - print_device(proxy, COLORED_NEW); - } } else if (!strcmp(interface, "org.bluez.Adapter1")) { ctrl_list = g_list_append(ctrl_list, proxy); @@ -360,39 +451,6 @@ } } -static void set_default_device(GDBusProxy *proxy, const char *attribute) -{ - char *desc = NULL; - DBusMessageIter iter; - const char *path; - - default_dev = proxy; - - if (proxy == NULL) { - default_attr = NULL; - goto done; - } - - if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) { - if (!g_dbus_proxy_get_property(proxy, "Address", &iter)) - goto done; - } - - path = g_dbus_proxy_get_path(proxy); - - dbus_message_iter_get_basic(&iter, &desc); - desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc, - attribute ? ":" : "", - attribute ? attribute + strlen(path) : ""); - -done: - rl_set_prompt(desc ? desc : PROMPT_ON); - printf("\r"); - rl_on_new_line(); - rl_redisplay(); - g_free(desc); -} - static void set_default_attribute(GDBusProxy *proxy) { const char *path; @@ -914,6 +972,36 @@ dbus_message_iter_close_container(iter, &value); } +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_entry(DBusMessageIter *dict, const char *key, int type, void *val) { @@ -936,76 +1024,69 @@ dbus_message_iter_close_container(dict, &entry); } +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); +} + #define DISTANCE_VAL_INVALID 0x7FFF struct set_discovery_filter_args { char *transport; dbus_uint16_t rssi; dbus_int16_t pathloss; - GSList *uuids; + char **uuids; + size_t uuids_len; }; -static void set_discovery_filter_setup(DBusMessageIter *iter, - void *user_data) +static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data) { struct set_discovery_filter_args *args = user_data; DBusMessageIter dict; 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 (args->uuids != NULL) { - DBusMessageIter entry, value, arrayIter; - char *uuids = "UUIDs"; - GSList *l; - - dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, - NULL, &entry); - /* dict key */ - dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, - &uuids); - - dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, - "as", &value); - - dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, "s", - &arrayIter); - - for (l = args->uuids; l != NULL; l = g_slist_next(l)) - /* list->data contains string representation of uuid */ - dbus_message_iter_append_basic(&arrayIter, - DBUS_TYPE_STRING, - &l->data); - - dbus_message_iter_close_container(&value, &arrayIter); + 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); - /* close vararg*/ - dbus_message_iter_close_container(&entry, &value); - - /* close entry */ - dbus_message_iter_close_container(&dict, &entry); - } + dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &args->uuids, + args->uuids_len); if (args->pathloss != DISTANCE_VAL_INVALID) dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16, - &args->pathloss); + &args->pathloss); if (args->rssi != DISTANCE_VAL_INVALID) dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16, &args->rssi); if (args->transport != NULL) dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING, - &args->transport); + &args->transport); dbus_message_iter_close_container(iter, &dict); } -static void set_discovery_filter_reply(DBusMessage *message, - void *user_data) +static void set_discovery_filter_reply(DBusMessage *message, void *user_data) { DBusError error; @@ -1021,7 +1102,8 @@ static gint filtered_scan_rssi = DISTANCE_VAL_INVALID; static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID; -static GSList *filtered_scan_uuids; +static char **filtered_scan_uuids; +static size_t filtered_scan_uuids_len; static char *filtered_scan_transport; static void cmd_set_scan_filter_commit(void) @@ -1033,6 +1115,7 @@ args.rssi = filtered_scan_rssi; args.transport = filtered_scan_transport; args.uuids = filtered_scan_uuids; + args.uuids_len = filtered_scan_uuids_len; if (check_default_ctrl() == FALSE) return; @@ -1047,25 +1130,22 @@ static void cmd_set_scan_filter_uuids(const char *arg) { - char *uuid_str, *saveptr, *uuids, *uuidstmp; - - g_slist_free_full(filtered_scan_uuids, g_free); + g_strfreev(filtered_scan_uuids); filtered_scan_uuids = NULL; + filtered_scan_uuids_len = 0; if (!arg || !strlen(arg)) - return; + goto commit; - uuids = g_strdup(arg); - for (uuidstmp = uuids; ; uuidstmp = NULL) { - uuid_str = strtok_r(uuidstmp, " \t", &saveptr); - if (uuid_str == NULL) - break; - filtered_scan_uuids = g_slist_append(filtered_scan_uuids, - strdup(uuid_str)); + filtered_scan_uuids = g_strsplit(arg, " ", -1); + if (!filtered_scan_uuids) { + rl_printf("Failed to parse input\n"); + return; } - g_free(uuids); + filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids); +commit: cmd_set_scan_filter_commit(); } @@ -1105,17 +1185,35 @@ cmd_set_scan_filter_commit(); } +static void clear_discovery_filter_setup(DBusMessageIter *iter, void *user_data) +{ + DBusMessageIter dict; + + 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 cmd_set_scan_filter_clear(const char *arg) { /* set default values for all options */ filtered_scan_rssi = DISTANCE_VAL_INVALID; filtered_scan_pathloss = DISTANCE_VAL_INVALID; - g_slist_free_full(filtered_scan_uuids, g_free); + g_strfreev(filtered_scan_uuids); filtered_scan_uuids = NULL; + filtered_scan_uuids_len = 0; g_free(filtered_scan_transport); filtered_scan_transport = NULL; - cmd_set_scan_filter_commit(); + if (g_dbus_proxy_method_call(default_ctrl, "SetDiscoveryFilter", + clear_discovery_filter_setup, set_discovery_filter_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to clear discovery filter\n"); + } } static struct GDBusProxy *find_device(const char *arg) @@ -1314,10 +1412,24 @@ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); } +static void remove_device(GDBusProxy *proxy) +{ + char *path; + + path = g_strdup(g_dbus_proxy_get_path(proxy)); + + if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice", + remove_device_setup, + remove_device_reply, + path, g_free) == FALSE) { + rl_printf("Failed to remove device\n"); + g_free(path); + } +} + static void cmd_remove(const char *arg) { GDBusProxy *proxy; - char *path; if (!arg || !strlen(arg)) { rl_printf("Missing device address argument\n"); @@ -1327,22 +1439,25 @@ if (check_default_ctrl() == FALSE) return; + if (strcmp(arg, "*") == 0) { + GList *list; + + for (list = g_list_first(dev_list); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + + remove_device(proxy); + } + + return; + } + proxy = find_proxy_by_address(dev_list, arg); if (!proxy) { rl_printf("Device %s not available\n", arg); return; } - path = g_strdup(g_dbus_proxy_get_path(proxy)); - - if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice", - remove_device_setup, - remove_device_reply, - path, g_free) == FALSE) { - rl_printf("Failed to remove device\n"); - g_free(path); - return; - } + remove_device(proxy); } static void connect_reply(DBusMessage *message, void *user_data) @@ -1865,38 +1980,6 @@ free(input); } -static gboolean input_handler(GIOChannel *channel, GIOCondition condition, - gpointer user_data) -{ - if (condition & G_IO_IN) { - rl_callback_read_char(); - return TRUE; - } - - if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { - g_main_loop_quit(main_loop); - return FALSE; - } - - return TRUE; -} - -static guint setup_standard_input(void) -{ - GIOChannel *channel; - guint source; - - channel = g_io_channel_unix_new(fileno(stdin)); - - source = g_io_add_watch(channel, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - input_handler, NULL); - - g_io_channel_unref(channel); - - return source; -} - static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, gpointer user_data) { @@ -2007,9 +2090,8 @@ static void client_ready(GDBusClient *client, void *user_data) { - guint *input = user_data; - - *input = setup_standard_input(); + if (!input) + input = setup_standard_input(); } int main(int argc, char *argv[]) @@ -2041,6 +2123,7 @@ main_loop = g_main_loop_new(NULL, FALSE); dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); + setlinebuf(stdout); rl_attempted_completion_function = cmd_completion; rl_erase_empty_line = 1; @@ -2059,8 +2142,7 @@ g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, property_changed, NULL); - input = 0; - g_dbus_client_set_ready_watch(client, client_ready, &input); + g_dbus_client_set_ready_watch(client, client_ready, NULL); g_main_loop_run(main_loop); diff -Nru bluez-5.37/configure bluez-5.41/configure --- bluez-5.37/configure 2015-12-28 02:14:15.000000000 +0000 +++ bluez-5.41/configure 2016-07-17 21:14:13.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bluez 5.37. +# Generated by GNU Autoconf 2.69 for bluez 5.41. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ # Identity of this package. PACKAGE_NAME='bluez' PACKAGE_TARNAME='bluez' -PACKAGE_VERSION='5.37' -PACKAGE_STRING='bluez 5.37' +PACKAGE_VERSION='5.41' +PACKAGE_STRING='bluez 5.41' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -642,6 +642,8 @@ CONFIGDIR SIXAXIS_FALSE SIXAXIS_TRUE +DEPRECATED_FALSE +DEPRECATED_TRUE EXPERIMENTAL_FALSE EXPERIMENTAL_TRUE MANPAGES_FALSE @@ -854,6 +856,7 @@ enable_datafiles enable_manpages enable_experimental +enable_deprecated enable_sixaxis enable_android ' @@ -1423,7 +1426,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bluez 5.37 to adapt to many kinds of systems. +\`configure' configures bluez 5.41 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1493,7 +1496,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bluez 5.37:";; + short | recursive ) echo "Configuration of bluez 5.41:";; esac cat <<\_ACEOF @@ -1532,6 +1535,7 @@ --disable-datafiles do not install configuration and data files --enable-manpages enable building of manual pages --enable-experimental enable experimental plugins (SAP, NFC, ...) + --enable-deprecated enable deprecated plugins (BLE services, ...) --enable-sixaxis enable sixaxis plugin --enable-android enable BlueZ for Android @@ -1653,7 +1657,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bluez configure 5.37 +bluez configure 5.41 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2018,7 +2022,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bluez $as_me 5.37, which was +It was created by bluez $as_me 5.41, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2882,7 +2886,7 @@ # Define the identity of the package. PACKAGE='bluez' - VERSION='5.37' + VERSION='5.41' cat >>confdefs.h <<_ACEOF @@ -13882,6 +13886,20 @@ fi +# Check whether --enable-deprecated was given. +if test "${enable_deprecated+set}" = set; then : + enableval=$enable_deprecated; enable_deprecated=${enableval} +fi + + if test "${enable_deprecated}" = "yes"; then + DEPRECATED_TRUE= + DEPRECATED_FALSE='#' +else + DEPRECATED_TRUE='#' + DEPRECATED_FALSE= +fi + + # Check whether --enable-sixaxis was given. if test "${enable_sixaxis+set}" = set; then : enableval=$enable_sixaxis; enable_sixaxis=${enableval} @@ -14313,6 +14331,10 @@ as_fn_error $? "conditional \"EXPERIMENTAL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${DEPRECATED_TRUE}" && test -z "${DEPRECATED_FALSE}"; then + as_fn_error $? "conditional \"DEPRECATED\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${SIXAXIS_TRUE}" && test -z "${SIXAXIS_FALSE}"; then as_fn_error $? "conditional \"SIXAXIS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -14718,7 +14740,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bluez $as_me 5.37, which was +This file was extended by bluez $as_me 5.41, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14784,7 +14806,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bluez config.status 5.37 +bluez config.status 5.41 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru bluez-5.37/configure.ac bluez-5.41/configure.ac --- bluez-5.37/configure.ac 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/configure.ac 2016-07-17 21:13:15.000000000 +0000 @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(bluez, 5.37) +AC_INIT(bluez, 5.41) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) @@ -247,6 +247,11 @@ [enable_experimental=${enableval}]) AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes") +AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated], + [enable deprecated plugins (BLE services, ...)]), + [enable_deprecated=${enableval}]) +AM_CONDITIONAL(DEPRECATED, test "${enable_deprecated}" = "yes") + AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis], [enable sixaxis plugin]), [enable_sixaxis=${enableval}]) AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" && diff -Nru bluez-5.37/debian/50-bluetooth-hci-auto-poweron.rules bluez-5.41/debian/50-bluetooth-hci-auto-poweron.rules --- bluez-5.37/debian/50-bluetooth-hci-auto-poweron.rules 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/50-bluetooth-hci-auto-poweron.rules 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -# Set bluetooth power up -ACTION=="add", SUBSYSTEM=="bluetooth", KERNEL=="hci[0-9]*", RUN+="/bin/hciconfig %k up" diff -Nru bluez-5.37/debian/bluez.install bluez-5.41/debian/bluez.install --- bluez-5.37/debian/bluez.install 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/bluez.install 2016-07-21 14:12:31.000000000 +0000 @@ -1,4 +1,4 @@ -src/main.conf etc/bluetooth +debian/main.conf etc/bluetooth profiles/input/input.conf etc/bluetooth # profiles/audio/audio.conf etc/bluetooth profiles/network/network.conf etc/bluetooth @@ -25,7 +25,6 @@ lib/udev/hid2hci lib/udev/rules.d/97-hid2hci.rules attrib/gatttool usr/bin -debian/50-bluetooth-hci-auto-poweron.rules lib/udev/rules.d #-- for systemd lib/systemd/system/bluetooth.service etc/dbus-1/system.d/bluetooth.conf diff -Nru bluez-5.37/debian/changelog bluez-5.41/debian/changelog --- bluez-5.37/debian/changelog 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/changelog 2016-07-21 16:47:26.000000000 +0000 @@ -1,3 +1,22 @@ +bluez (5.41-0ubuntu1.1) xenial; urgency=medium + + [ Konrad Zapałowicz ] + * New upstream version + + [ Simon Fels ] + + * debian/50-bluetooth-hci-auto-poweron.rules: + - Get rid of legacy udev rule to power on bluetooth controllers on + startup. Using hciconfig for that is the legacy way and will lead + to different problems as the kernel side stack wont initialize + all needed subsystems when used. BlueZ introduced a replacement + for this with 5.36 which basically tells the bluetoothd through + a config option to turn on all found Bluetooth controllers by + default. With changing this there should be no change in terms + of functionality. + + -- Simon Fels Thu, 21 Jul 2016 18:46:49 +0200 + bluez (5.37-0ubuntu5) xenial; urgency=medium [Tony Espy ] diff -Nru bluez-5.37/debian/main.conf bluez-5.41/debian/main.conf --- bluez-5.37/debian/main.conf 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.41/debian/main.conf 2016-07-21 14:12:31.000000000 +0000 @@ -0,0 +1,89 @@ +[General] + +# Default adaper name +# Defaults to 'BlueZ X.YZ' +#Name = BlueZ + +# Default device class. Only the major and minor device class bits are +# considered. Defaults to '0x000000'. +#Class = 0x000100 + +# How long to stay in discoverable mode before going back to non-discoverable +# The value is in seconds. Default is 180, i.e. 3 minutes. +# 0 = disable timer, i.e. stay discoverable forever +#DiscoverableTimeout = 0 + +# How long to stay in pairable mode before going back to non-discoverable +# The value is in seconds. Default is 0. +# 0 = disable timer, i.e. stay pairable forever +#PairableTimeout = 0 + +# Automatic connection for bonded devices driven by platform/user events. +# If a platform plugin uses this mechanism, automatic connections will be +# enabled during the interval defined below. Initially, this feature +# intends to be used to establish connections to ATT channels. Default is 60. +#AutoConnectTimeout = 60 + +# Use vendor id source (assigner), vendor, product and version information for +# DID profile support. The values are separated by ":" and assigner, VID, PID +# and version. +# Possible vendor id source values: bluetooth, usb (defaults to usb) +#DeviceID = bluetooth:1234:5678:abcd + +# Do reverse service discovery for previously unknown devices that connect to +# us. This option is really only needed for qualification since the BITE tester +# doesn't like us doing reverse SDP for some test cases (though there could in +# theory be other useful purposes for this too). Defaults to 'true'. +#ReverseServiceDiscovery = true + +# Enable name resolving after inquiry. Set it to 'false' if you don't need +# remote devices name and want shorter discovery cycle. Defaults to 'true'. +#NameResolving = true + +# Enable runtime persistency of debug link keys. Default is false which +# makes debug link keys valid only for the duration of the connection +# that they were created for. +#DebugKeys = false + +# Restricts all controllers to the specified transport. Default value +# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). +# Possible values: "dual", "bredr", "le" +#ControllerMode = dual + +# Enables Multi Profile Specification support. This allows to specify if +# system supports only Multiple Profiles Single Device (MPSD) configuration +# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple +# Devices (MPMD) configurations. +# Possible values: "off", "single", "multiple" +#MultiProfile = off + +# Permanently enables the Fast Connectable setting for adapters that +# support it. When enabled other devices can connect faster to us, +# however the tradeoff is increased power consumptions. This feature +# will fully work only on kernel version 4.1 and newer. Defaults to +# 'false'. +#FastConnectable = false + +[Policy] + +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# 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 + +# ReconnectAttempts define the number of attempts to reconnect after a link +# lost. Setting the value to 0 disables reconnecting feature. +#ReconnectAttempts=7 + +# ReconnectIntervals define the set of intervals in seconds to use in between +# 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 + +# AutoEnable defines option to enable all controllers when they are found. +# This includes adapters present on start as well as adapters that are plugged +# in later on. Defaults to 'false'. +AutoEnable=true diff -Nru bluez-5.37/debian/patches/0001-profiles-audio-add-big-endian-structure-definition-f.patch bluez-5.41/debian/patches/0001-profiles-audio-add-big-endian-structure-definition-f.patch --- bluez-5.37/debian/patches/0001-profiles-audio-add-big-endian-structure-definition-f.patch 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/patches/0001-profiles-audio-add-big-endian-structure-definition-f.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -From d3056811f75aa788382f56848e3a871ae3cbbf33 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Thu, 11 Feb 2016 14:45:19 +0100 -Subject: [PATCH] profiles: audio: add big endian structure definition for LDAC - codec - ---- - profiles/audio/a2dp-codecs.h | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/profiles/audio/a2dp-codecs.h b/profiles/audio/a2dp-codecs.h -index e9da0bf..4fb5c0c 100644 ---- a/profiles/audio/a2dp-codecs.h -+++ b/profiles/audio/a2dp-codecs.h -@@ -234,6 +234,11 @@ typedef struct { - uint8_t channel_mode:4; - } __attribute__ ((packed)) a2dp_aptx_t; - -+typedef struct { -+ a2dp_vendor_codec_t info; -+ uint8_t unknown[2]; -+} __attribute__ ((packed)) a2dp_ldac_t; -+ - #else - #error "Unknown byte order" - #endif --- -2.5.0 - diff -Nru bluez-5.37/debian/patches/0005-audio-media-return-empty-string-when-player-name-is-.patch bluez-5.41/debian/patches/0005-audio-media-return-empty-string-when-player-name-is-.patch --- bluez-5.37/debian/patches/0005-audio-media-return-empty-string-when-player-name-is-.patch 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/patches/0005-audio-media-return-empty-string-when-player-name-is-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -From c9055958af1dec734b66bf4c941ee7392034c958 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Wed, 2 Dec 2015 11:58:54 +0100 -Subject: [PATCH 5/5] audio/media: return empty string when player name is not - available - -If the name for the player is set as NULL we will crash later on -so taking a valid default is required. - -Crash observed without this: - -Program terminated with signal SIGSEGV, Segmentation fault. -0 strlen () at ../sysdeps/arm/armv6t2/strlen.S:85 -85 ../sysdeps/arm/armv6t2/strlen.S: No such file or directory. -(gdb) bt -0 strlen () at ../sysdeps/arm/armv6t2/strlen.S:85 -1 0xb6f13ebc in avrcp_handle_media_player_list (session=0xb8f339c0, - session=0xb8f339c0, end_item=, start_item=, - pdu=0xb8f43283) at profiles/audio/avrcp.c:1917 -2 avrcp_handle_get_folder_items (session=0xb8f339c0, pdu=0xb8f43283, - transaction=) at profiles/audio/avrcp.c:1968 -3 0xb6f13fb8 in handle_browsing_pdu (conn=, - transaction=, operands=0xb8f43283 "q\b", - operand_count=, user_data=0xb8f339c0) - at profiles/audio/avrcp.c:2028 -4 0xb6f0f088 in session_browsing_cb (chan=, - cond=, data=0xb8f3ba28) at profiles/audio/avctp.c:930 -5 0xb6e19e90 in g_main_dispatch (context=0xb8f28100) - at /build/buildd/glib2.0-2.44.1/./glib/gmain.c:3122 -6 g_main_context_dispatch (context=context@entry=0xb8f28100) - at /build/buildd/glib2.0-2.44.1/./glib/gmain.c:3737 -7 0xb6e1a114 in g_main_context_iterate (context=0xb8f28100, - block=block@entry=1, dispatch=dispatch@entry=1, self=) - at /build/buildd/glib2.0-2.44.1/./glib/gmain.c:3808 -8 0xb6e1a3b0 in g_main_loop_run (loop=0xb8f28030) - at /build/buildd/glib2.0-2.44.1/./glib/gmain.c:4002 -9 0xb6efef3e in main (argc=1, argv=0xbebdde04) at src/main.c:661 ---- - profiles/audio/media.c | 3 +++ - 1 file changed, 3 insertions(+) - -Index: bluez-5.37/profiles/audio/media.c -=================================================================== ---- bluez-5.37.orig/profiles/audio/media.c -+++ bluez-5.37/profiles/audio/media.c -@@ -1018,6 +1018,9 @@ static const char *get_player_name(void - { - struct media_player *mp = user_data; - -+ if (!mp->name) -+ return ""; -+ - return mp->name; - } - diff -Nru bluez-5.37/debian/patches/series bluez-5.41/debian/patches/series --- bluez-5.37/debian/patches/series 2016-03-01 15:23:48.000000000 +0000 +++ bluez-5.41/debian/patches/series 2016-07-21 14:12:31.000000000 +0000 @@ -14,5 +14,3 @@ # dropped with one of the next releases 0003-tools-mpris-proxy-allow-user-to-specify-different-mp.patch 0004-obexd-enable-ebook-backend-conditionally.patch -0005-audio-media-return-empty-string-when-player-name-is-.patch -0001-profiles-audio-add-big-endian-structure-definition-f.patch diff -Nru bluez-5.37/doc/adapter-api.txt bluez-5.41/doc/adapter-api.txt --- bluez-5.37/doc/adapter-api.txt 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/doc/adapter-api.txt 2016-07-17 21:13:15.000000000 +0000 @@ -80,7 +80,11 @@ When discovery filter is set, Device objects will be created as new devices with matching criteria are - discovered. PropertiesChanged signals will be emitted + discovered regardless of they are connectable or + discoverable which enables listening to + non-connectable and non-discoverable devices. + + PropertiesChanged signals will be emitted for already existing Device objects, with updated RSSI value. If one or more discovery filters have been set, the RSSI delta-threshold, that is imposed by @@ -102,6 +106,7 @@ right after call to StartDiscovery. Possible errors: org.bluez.Error.NotReady + org.bluez.Error.NotSupported org.bluez.Error.Failed Properties string Address [readonly] diff -Nru bluez-5.37/doc/advertising-api.txt bluez-5.41/doc/advertising-api.txt --- bluez-5.37/doc/advertising-api.txt 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/doc/advertising-api.txt 2016-03-18 02:18:03.000000000 +0000 @@ -3,7 +3,7 @@ Advertising packets are structured data which is broadcast on the LE Advertising channels and available for all devices in range. Because of the limited space -available in LE Advertising packets (32 bytes), each packet's contents must be +available in LE Advertising packets (31 bytes), each packet's contents must be carefully controlled. BlueZ acts as a store for the Advertisement Data which is meant to be sent. diff -Nru bluez-5.37/doc/btsnoop.txt bluez-5.41/doc/btsnoop.txt --- bluez-5.37/doc/btsnoop.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.41/doc/btsnoop.txt 2016-05-26 16:51:11.000000000 +0000 @@ -0,0 +1,178 @@ +BTSnoop/Monitor protocol formats +******************************** + +Opcode definitions +================== + +New Index +--------- + + Code: 0x0000 + Parameters: Type (1 Octet + Bus (1 Octet) + BD_Addr (6 Octets) + Name (8 Octets) + + This opcode indicates that a new controller instance with a + given index was added. With some protocols, like the TTY-based + one there is only a single supported controller, meaning the + index is implicitly 0. + +Deleted Index +------------- + + Code: 0x0001 + + This opcode indicates that the controller with a specific index + was removed. + +Command Packet +-------------- + + Code: 0x0002 + + HCI command packet. + +Event Packet +------------ + + Code: 0x0003 + + HCI event packet. + +ACL TX Packet +------------- + + Code: 0x0004 + + Outgoing ACL packet. + +ACL RX Packet +------------- + + Code: 0x0005 + + Incoming ACL packet. + +SCO TX Packet +-------------- + + Code: 0x0006 + + Outgoing SCO packet. + +SCO RX Packet +------------- + + Code: 0x0007 + + Incomnig SCO packet. + +Open Index +---------- + + Code: 0x0008 + + The HCI transport for the specified controller has been opened. + +Close Index +----------- + + Code: 0x0009 + + The HCI transport for the specified controller has been closed. + +Index Information +----------------- + + Code: 0x000a + Parameters: BD_Addr (6 Octets) + Manufacturer (2 Octets) + + Information about a specific controller. + +Vendor Diagnostics +------------------ + + Code: 0x000b + + Vendor diagnostic information. + +System Note +----------- + + Code: 0x000c + + System note. + +User Logging +------------ + + Code: 0x000d + Parameters: Priority (1 Octet) + Ident_Length (1 Octet) + Ident (Ident_Length Octets) + + User logging information. + + +TTY-based protocol +================== + +This section covers the protocol that can be parsed by btmon when +passing it the --tty parameter. The protocol is little endian, packet +based, and has the following header for each packet: + +struct tty_hdr { + uint16_t data_len; + uint16_t opcode; + uint8_t flags; + uint8_t hdr_len; + uint8_t ext_hdr[0]; +} __attribute__ ((packed)); + +The actual payload starts at ext_hdr + hdr_len and has the length of +data_len - 4 - hdr_len. Each field of the header is defined as follows: + +data_len: + This is the total length of the entire packet, excuding the + data_len field itself. + +opcode: + The BTSnoop opcode + +flags: + Special flags for the packet. Currently no flags are defined. + +hdr_len: + Length of the extended header. + +ext_hdr: + This is a sequence of header extension fields formatted as: + + struct { + uint8_t type; + uint8_t value[length]; + } + + The length of the value is dependent on the type. Currently the + following types are defined: + + Type Length Meaning + ---------------------------------------------------------------- + 1 Command drops 1 byte Dropped HCI command packets + 2 Event drops 1 byte Dropped HCI event packets + 3 ACL TX drops 1 byte Dropped ACL TX packets + 4 ACL RX drops 1 byte Dropped ACL RX packets + 5 SCO TX drops 1 byte Dropped SCO TX packets + 6 SCO RX drops 1 byte Dropped SCO RX packets + 7 Other drops 1 byte Dropped other packets + 8 32-bit timestamp 4 bytes Timestamp in 1/10th ms + + The drops fields indicate the number of packets that the + implementation had to drop (e.g. due to lack of buffers) since + the last reported drop count. + + The fields of the extended header must be sorted by increasing + type. This is essential so that unknown types can be ignored and + the parser can jump to processing the payload. diff -Nru bluez-5.37/doc/device-api.txt bluez-5.41/doc/device-api.txt --- bluez-5.37/doc/device-api.txt 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/doc/device-api.txt 2016-03-18 02:18:03.000000000 +0000 @@ -212,10 +212,7 @@ Service advertisement data. Keys are the UUIDs in string format followed by its byte array value. - array{object} GattServices [readonly, optional] + bool ServicesResolved [readonly, experimental] - List of GATT service object paths. Each referenced - object exports the org.bluez.GattService1 interface and - represents a remote GATT service. This property will be - updated once all remote GATT services of this device - have been discovered and exported over D-Bus. + Indicate whether or not service discovery has been + resolved. diff -Nru bluez-5.37/doc/gatt-api.txt bluez-5.41/doc/gatt-api.txt --- bluez-5.37/doc/gatt-api.txt 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/doc/gatt-api.txt 2016-05-26 16:51:11.000000000 +0000 @@ -45,14 +45,6 @@ belongs to. Only present on services from remote devices. - array{object} Characteristics [read-only] - - Array of object paths representing the characteristics - of this service. This property is set only when the - characteristic discovery has been completed, however the - characteristic objects will become available via - ObjectManager as soon as they get discovered. - array{object} Includes [read-only]: Not implemented Array of object paths representing the included @@ -69,23 +61,29 @@ Interface org.bluez.GattCharacteristic1 [Experimental] Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY -Methods array{byte} ReadValue() +Methods array{byte} ReadValue(dict options) Issues a request to read the value of the characteristic and returns the value if the operation was successful. + Possible options: "offset": uint16 offset + "device": Object Device (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted org.bluez.Error.NotAuthorized org.bluez.Error.NotSupported - void WriteValue(array{byte} value) + void WriteValue(array{byte} value, dict options) Issues a request to write the value of the characteristic. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted @@ -127,7 +125,7 @@ when a notification or indication is received, upon which a PropertiesChanged signal will be emitted. - boolean Notifying [read-only] + boolean Notifying [read-only, optional] True, if notifications or indications on this characteristic are currently enabled. @@ -152,15 +150,8 @@ "encrypt-write" "encrypt-authenticated-read" "encrypt-authenticated-write" - - array{object} Descriptors [read-only] - - Array of object paths representing the descriptors - of this service. This property is set only when the - descriptor discovery has been completed, however the - descriptor objects will become available via - ObjectManager as soon as they get discovered. - + "secure-read" (Server only) + "secure-write" (Server only) Characteristic Descriptors hierarchy ==================================== @@ -171,23 +162,29 @@ Interface org.bluez.GattDescriptor1 [Experimental] Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY/descriptorZZZ -Methods array{byte} ReadValue() +Methods array{byte} ReadValue(dict flags) Issues a request to read the value of the characteristic and returns the value if the operation was successful. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted org.bluez.Error.NotAuthorized org.bluez.Error.NotSupported - void WriteValue(array{byte} value) + void WriteValue(array{byte} value, dict flags) Issues a request to write the value of the characteristic. + Possible options: "offset": Start offset + "device": Device path (Server only) + Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress org.bluez.Error.NotPermitted @@ -222,9 +219,11 @@ "encrypt-write" "encrypt-authenticated-read" "encrypt-authenticated-write" + "secure-read" (Server Only) + "secure-write" (Server Only) -Profile hierarcy -================ +GATT Profile hierarcy +===================== Local profile (GATT client) instance. By registering this type of object an application effectively indicates support for a specific GATT profile @@ -243,6 +242,10 @@ profile, because when this method gets called it has already been unregistered. +Properties array{string} UUIDs [read-only] + + 128-bit GATT service UUIDs to auto connect. + GATT Manager hierarchy ====================== @@ -270,9 +273,9 @@ containing two separate GATT services may look like this: -> /com/example + | - org.freedesktop.DBus.ObjectManager | -> /com/example/service0 - | | - org.freedesktop.DBus.ObjectManager | | - org.freedesktop.DBus.Properties | | - org.bluez.GattService1 | | @@ -289,7 +292,6 @@ | - org.bluez.GattDescriptor1 | -> /com/example/service1 - | - org.freedesktop.DBus.ObjectManager | - org.freedesktop.DBus.Properties | - org.bluez.GattService1 | @@ -309,45 +311,22 @@ Interface org.bluez.GattManager1 [Experimental] Object path [variable prefix]/{hci0,hci1,...} -Methods void RegisterService(object service, dict options) - - Registers a local GATT service hierarchy as described - above. - - "service" object path together with the D-Bus system - bus connection ID define the identification of the - application registering a GATT based service. - - Possible errors: org.bluez.Error.InvalidArguments - org.bluez.Error.AlreadyExists - - void UnregisterService(object service) - - This unregisters the service that has been - previously registered. The object path parameter - must match the same value that has been used - on registration. - - Possible errors: org.bluez.Error.InvalidArguments - org.bluez.Error.DoesNotExist +Methods void RegisterApplication(object application, dict options) - void RegisterProfile(object profile, array{string} UUIDs, - dict options) + Registers a local GATT services hierarchy as described + above (GATT Server) and/or GATT profiles (GATT Client). - Registers a GATT (client role) profile exported - under interface GattProfile1. The array of UUIDs - specifies the mandatory set of remote service - UUIDs that should all be available for the - remote device to match this profile. Matching - devices will be added to the auto-connection - list and connected whenever available. + The application object path together with the D-Bus + system bus connection ID define the identification of + the application registering a GATT based + service or profile. Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.AlreadyExists - void UnregisterProfile(object profile) + void UnregisterApplication(object application) - This unregisters the profile that has been + This unregisters the services that has been previously registered. The object path parameter must match the same value that has been used on registration. diff -Nru bluez-5.37/doc/mgmt-api.txt bluez-5.41/doc/mgmt-api.txt --- bluez-5.37/doc/mgmt-api.txt 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/doc/mgmt-api.txt 2016-07-17 21:13:15.000000000 +0000 @@ -25,7 +25,9 @@ Linux kernel v3.19 Version 1.8 Linux kernel v4.1 Version 1.9 Linux kernel v4.2 Version 1.10 -Linux kernel v4.5 Version 1.11 (not yet released) +Linux kernel v4.5 Version 1.11 +Linux kernel v4.6 Version 1.12 +Linux kernel v4.8 Version 1.13 (not yet released) Version 1.1 introduces Set Device ID command. @@ -71,6 +73,12 @@ Version 1.11 introduces Get Advertising Size Information command. +Version 1.12 introduces a new limited privacy mode (value 0x02 passed to +the Set Privacy command). + +Version 1.13 introduces a new authentication failure reason code for the +Device Disconnected event. + Example ======= @@ -250,7 +258,7 @@ Name (249 Octets) Short_Name (11 Octets) - This command is used to retreive the current state and basic + 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. @@ -547,6 +555,7 @@ Invalid Parameters Invalid Index + Set High Speed Command ====================== @@ -762,8 +771,8 @@ This command is used to feed the kernel with currently known link keys. The command does not need to be called again upon the - receiption of New Link Key events since the kernel updates its - list automatically. + receipt of New Link Key events since the kernel updates its list + automatically. The Debug_Keys parameter is used to tell the kernel whether to accept the usage of debug keys or not. The allowed values for @@ -826,7 +835,7 @@ This command is used to feed the kernel with currently known (SMP) Long Term Keys. The command does not need to be called - again upon the receiption of New Long Term Key events since the + again upon the receipt of New Long Term Key events since the kernel updates its list automatically. Possible values for the Address_Type parameter: @@ -1023,7 +1032,7 @@ This command is used to trigger pairing with a remote device. The IO_Capability command parameter is used to temporarily (for - this pairing event only) override the global IO Capaility (set + this pairing event only) override the global IO Capability (set using the Set IO Capability command). Possible values for the Address_Type parameter: @@ -1567,7 +1576,7 @@ 0x0000 Disable Device ID 0x0001 Bluetooth SIG - 0x0002 USB Implementer’s Forum + 0x0002 USB Implementer's Forum The information is put into the EIR data. If the controller does not support EIR or if SSP is disabled, this command will still @@ -1600,7 +1609,7 @@ Using value 0x01 means that when connectable setting is disabled, the advertising happens with undirected non-connectable advertising - packets and a non-resovable random address is used. If connectable + packets and a non-resolvable random address is used. If connectable setting is enabled, then undirected connectable advertising packets and the identity address or resolvable private address are used. @@ -1821,13 +1830,12 @@ means resolvable private address is used when the controller is discoverable and also when pairing is initiated. - With value 0x02 the kernel will use privacy mode with resolvable - private address. In case the conroller is bondable and discoverable - the identity address is used. Also when pairing is initiated, the - connection will be established with the identity address. + With value 0x02 the kernel will use a limited privacy mode with a + resolvable private address except when the controller is bondable + and discoverable, in which case the identity address is used. Exposing the identity address when bondable and discoverable or - during initated pairing can be a privacy issue. For dual-mode + during initiated pairing can be a privacy issue. For dual-mode controllers this can be neglected since its public address will be exposed over BR/EDR anyway. The benefit of exposing the identity address for pairing purposes is that it makes matching @@ -1841,7 +1849,7 @@ When the controller has a public address (mandatory for dual-mode controllers) it is used as identity address. In case the controller is single mode LE only without a public address, it is required - to configure a static random andress first. The privacy mode can + to configure a static random address first. The privacy mode can only be enabled when an identity address is available. The Identity_Resolving_Key is the local key assigned for the local @@ -1870,7 +1878,7 @@ This command is used to feed the kernel with currently known identity resolving keys. The command does not need to be called - again upon the receiption of New Identity Resolving Key events + again upon the receipt of New Identity Resolving Key events since the kernel updates its list automatically. Possible values for the Address_Type parameter: @@ -2063,7 +2071,7 @@ Address (6 Octets) Address_Type (1 Octet) Min_Connection_Interval (2 Octets) - Max_Connection_Interval (2 Octes) + Max_Connection_Interval (2 Octets) Connection_Latency (2 Octets) Supervision_Timeout (2 Octets) } @@ -2133,7 +2141,7 @@ Supported_Options (4 Octets) Missing_Options (4 Octets) - This command is used to retreive the supported configuration + This command is used to retrieve the supported configuration options of a controller and the missing configuration options. The missing options are required to be configured before the @@ -2194,7 +2202,7 @@ can be expected that it will be announced via Index Added event. Wrongly configured controllers might still cause an error when - trying to power them via Set Powered commmand. + trying to power them via Set Powered command. This command generates a Command Complete event on success or a Command Status event on failure. @@ -2431,7 +2439,7 @@ 0x01 Unconfigured Controller (BR/EDR and/or LE) 0x02 Alternate MAC/PHY Controller (AMP) - The 0x00 and 0x01 types indiciate a primary BR/EDR and/or LE + The 0x00 and 0x01 types indicate a primary BR/EDR and/or LE controller. The difference is just if they need extra configuration or if they are fully configured. @@ -2452,6 +2460,8 @@ 0x04 RS232 0x05 PCI 0x06 SDIO + 0x07 SPI + 0x08 I2C Controllers marked as RAW only operation are currently not listed by this command. @@ -2522,7 +2532,7 @@ not be set. The Flags bit 5 indicates support for automatically adding the - Apperance value to the scan response data. Users of this flag + Appearance value to the scan response data. Users of this flag will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance field will be added in front of the scan response data provided by the user. If the appearance value is not supported, then this @@ -2728,7 +2738,7 @@ Max_Scan_Rsp_Len (1 Octet) The Read Advertising Features command returns the overall maximum - size of advertising data and scan respone data fields. That size is + size of advertising data and scan response data fields. That size is valid when no Flags are used. However when certain Flags are used, then the size might decrease. This command can be used to request detailed information about the maximum available size. @@ -2781,9 +2791,9 @@ 7 BR/EDR/LE (interleaved discovery) The limited discovery uses active scanning for Low Energy scanning - and will search for devices with the limited discoverabilty flag + and will search for devices with the limited discoverability flag configured. On BR/EDR it uses LIAC and filters on the limited - discoverabilty flag of the class of device. + discoverability flag of the class of device. When the discovery procedure starts the Discovery event will notify this similar to Start Discovery. @@ -2922,7 +2932,7 @@ PIN_Length (1 Octet) } - This event indicates that a new link key has bee generated for a + This event indicates that a new link key has been generated for a remote device. The Store_Hint parameter indicates whether the host is expected @@ -2948,7 +2958,7 @@ 0x07 Unauthenticated Combination key from P-256 0x08 Authenticated Combination key from P-256 - Receiving this event indicates that a pairing procecure has + Receiving this event indicates that a pairing procedure has been completed. @@ -2996,7 +3006,7 @@ 0x03 Authenticated key from P-256 0x04 Debug key from P-256 - Receiving this event indicates that a pairing procecure has + Receiving this event indicates that a pairing procedure has been completed. @@ -3059,6 +3069,7 @@ 1 Connection timeout 2 Connection terminated by local host 3 Connection terminated by remote host + 4 Connection terminated due to authentication failure Note that the local/remote distinction just determines which side terminated the low-level connection, regardless of the @@ -3301,7 +3312,7 @@ 2 LE Random For devices using resolvable random addresses with a known - identity resolving key, the event paramters will contain + identity resolving key, the event parameters will contain the identity. After receiving this event, the device will become essentially private again. @@ -3479,7 +3490,7 @@ Address (6 Octets) Address_Type (1 Octet) Min_Connection_Interval (2 Octets) - Max_Connection_Interval (2 Octes) + Max_Connection_Interval (2 Octets) Connection_Latency (2 Octets) Supervision_Timeout (2 Octets) } diff -Nru bluez-5.37/doc/settings-storage.txt bluez-5.41/doc/settings-storage.txt --- bluez-5.37/doc/settings-storage.txt 2015-09-28 12:16:53.000000000 +0000 +++ bluez-5.41/doc/settings-storage.txt 2016-05-26 16:51:11.000000000 +0000 @@ -175,6 +175,7 @@ 2803:value_handle:properties:uuid Descriptor: + value:uuid uuid Sample Attributes section: diff -Nru bluez-5.37/doc/supported-features.txt bluez-5.41/doc/supported-features.txt --- bluez-5.37/doc/supported-features.txt 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/doc/supported-features.txt 2016-07-17 21:13:15.000000000 +0000 @@ -14,7 +14,7 @@ GATT 4.2 Server, Client SDAP 1.1 Server, Client RFCOMM 1.1 Server, Client -SPP 1.1 Server, Client +SPP 1.2 Server, Client PXP 1.0 Reporter, Monitor HOGP 1.0 Host diff -Nru bluez-5.37/doc/test-coverage.txt bluez-5.41/doc/test-coverage.txt --- bluez-5.37/doc/test-coverage.txt 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/doc/test-coverage.txt 2016-07-17 21:13:15.000000000 +0000 @@ -13,7 +13,7 @@ test-sdp 133 SDP qualification test cases test-uuid 30 UUID conversion handling test-mgmt 9 Management interface handling -test-crypto 4 Cryptographic toolbox helpers +test-crypto 5 Cryptographic toolbox helpers test-textfile 4 Old textfile storage format test-ringbuf 3 Ring buffer functionality test-queue 6 Queue handling functionality @@ -39,7 +39,7 @@ Application Count Description ------------------------------------------- -mgmt-tester 305 Kernel management interface testing +mgmt-tester 307 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 ----- - 382 + 384 Android end-to-end testing diff -Nru bluez-5.37/emulator/btdev.c bluez-5.41/emulator/btdev.c --- bluez-5.37/emulator/btdev.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/emulator/btdev.c 2016-07-17 21:13:15.000000000 +0000 @@ -3098,6 +3098,7 @@ } cmd_status(btdev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_GENERATE_DHKEY); + dh_evt.status = BT_HCI_ERR_SUCCESS; le_meta_event(btdev, BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE, &dh_evt, sizeof(dh_evt)); break; diff -Nru bluez-5.37/emulator/bthost.c bluez-5.41/emulator/bthost.c --- bluez-5.37/emulator/bthost.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/emulator/bthost.c 2016-07-17 21:13:15.000000000 +0000 @@ -2329,6 +2329,11 @@ &cmd, sizeof(cmd)); } +bool bthost_bredr_capable(struct bthost *bthost) +{ + return lmp_bredr_capable(bthost); +} + void bthost_request_auth(struct bthost *bthost, uint16_t handle) { struct btconn *conn; diff -Nru bluez-5.37/emulator/bthost.h bluez-5.41/emulator/bthost.h --- bluez-5.37/emulator/bthost.h 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/emulator/bthost.h 2016-07-17 21:13:15.000000000 +0000 @@ -108,6 +108,8 @@ void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject); bool bthost_get_reject_user_confirm(struct bthost *bthost); +bool bthost_bredr_capable(struct bthost *bthost); + uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle); typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid, diff -Nru bluez-5.37/emulator/hciemu.c bluez-5.41/emulator/hciemu.c --- bluez-5.37/emulator/hciemu.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/emulator/hciemu.c 2016-07-17 21:13:15.000000000 +0000 @@ -243,7 +243,8 @@ } create_req[0] = HCI_VENDOR_PKT; - create_req[1] = HCI_BREDR; + create_req[1] = HCI_PRIMARY; + written = write(fd, create_req, sizeof(create_req)); if (written < 0) { close(fd); diff -Nru bluez-5.37/emulator/le.c bluez-5.41/emulator/le.c --- bluez-5.37/emulator/le.c 2015-07-30 19:17:18.000000000 +0000 +++ bluez-5.41/emulator/le.c 2016-07-17 21:13:15.000000000 +0000 @@ -1881,7 +1881,7 @@ } setup_cmd[0] = HCI_VENDOR_PKT; - setup_cmd[1] = HCI_BREDR; + setup_cmd[1] = HCI_PRIMARY; if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) { close(hci->vhci_fd); diff -Nru bluez-5.37/emulator/smp.c bluez-5.41/emulator/smp.c --- bluez-5.37/emulator/smp.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/emulator/smp.c 2016-07-17 21:13:15.000000000 +0000 @@ -68,8 +68,6 @@ #define DIST_SIGN 0x04 #define DIST_LINK_KEY 0x08 -#define KEY_DIST (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN) - #define SC_NO_DIST (DIST_ENC_KEY | DIST_LINK_KEY) #define MAX_IO_CAP 0x04 @@ -193,6 +191,14 @@ return method; } +static uint8_t key_dist(struct bthost *host) +{ + if (!bthost_bredr_capable(host)) + return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN); + + return (DIST_ENC_KEY | DIST_ID_KEY | DIST_SIGN | DIST_LINK_KEY); +} + static void smp_send(struct smp_conn *conn, uint8_t smp_cmd, const void *data, uint8_t len) { @@ -433,8 +439,8 @@ } rsp.max_key_size = 0x10; - rsp.init_key_dist = conn->preq[5] & KEY_DIST; - rsp.resp_key_dist = conn->preq[6] & KEY_DIST; + rsp.init_key_dist = conn->preq[5] & key_dist(bthost); + rsp.resp_key_dist = conn->preq[6] & key_dist(bthost); conn->prsp[0] = BT_L2CAP_SMP_PAIRING_RESPONSE; memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); @@ -691,8 +697,8 @@ req.oob_data = 0x00; req.auth_req = auth_req; req.max_key_size = 0x10; - req.init_key_dist = KEY_DIST; - req.resp_key_dist = KEY_DIST; + req.init_key_dist = key_dist(conn->smp->bthost); + req.resp_key_dist = key_dist(conn->smp->bthost); conn->preq[0] = BT_L2CAP_SMP_PAIRING_REQUEST; memcpy(&conn->preq[1], &req, sizeof(req)); @@ -818,8 +824,8 @@ memset(&req, 0, sizeof(req)); req.max_key_size = 0x10; - req.init_key_dist = KEY_DIST; - req.resp_key_dist = KEY_DIST; + req.init_key_dist = key_dist(smp->bthost); + req.resp_key_dist = key_dist(smp->bthost); smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req)); } diff -Nru bluez-5.37/emulator/vhci.c bluez-5.41/emulator/vhci.c --- bluez-5.37/emulator/vhci.c 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/emulator/vhci.c 2016-07-17 21:13:15.000000000 +0000 @@ -105,15 +105,15 @@ switch (type) { case VHCI_TYPE_BREDRLE: btdev_type = BTDEV_TYPE_BREDRLE; - ctrl_type = HCI_BREDR; + ctrl_type = HCI_PRIMARY; break; case VHCI_TYPE_BREDR: btdev_type = BTDEV_TYPE_BREDR; - ctrl_type = HCI_BREDR; + ctrl_type = HCI_PRIMARY; break; case VHCI_TYPE_LE: btdev_type = BTDEV_TYPE_LE; - ctrl_type = HCI_BREDR; + ctrl_type = HCI_PRIMARY; break; case VHCI_TYPE_AMP: btdev_type = BTDEV_TYPE_AMP; diff -Nru bluez-5.37/gdbus/client.c bluez-5.41/gdbus/client.c --- bluez-5.37/gdbus/client.c 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/gdbus/client.c 2016-03-18 02:18:03.000000000 +0000 @@ -853,28 +853,30 @@ if (client == NULL) return FALSE; - data = g_try_new0(struct method_call_data, 1); - if (data == NULL) - return FALSE; - - data->function = function; - data->user_data = user_data; - data->destroy = destroy; - msg = dbus_message_new_method_call(client->service_name, proxy->obj_path, proxy->interface, method); - if (msg == NULL) { - g_free(data); + if (msg == NULL) return FALSE; - } if (setup) { DBusMessageIter iter; dbus_message_iter_init_append(msg, &iter); - setup(&iter, data->user_data); + setup(&iter, user_data); } + if (!function) + return g_dbus_send_message(client->dbus_conn, msg); + + data = g_try_new0(struct method_call_data, 1); + if (data == NULL) + return FALSE; + + data->function = function; + data->user_data = user_data; + data->destroy = destroy; + + if (g_dbus_send_message_with_reply(client->dbus_conn, msg, &call, METHOD_CALL_TIMEOUT) == FALSE) { dbus_message_unref(msg); @@ -1073,9 +1075,6 @@ dbus_message_iter_next(&dict); } - - if (client->ready) - client->ready(client, client->ready_data); } static void get_managed_objects_reply(DBusPendingCall *call, void *user_data) @@ -1096,6 +1095,9 @@ parse_managed_objects(client, reply); done: + if (client->ready) + client->ready(client, client->ready_data); + dbus_message_unref(reply); dbus_pending_call_unref(client->get_objects_call); diff -Nru bluez-5.37/gdbus/gdbus.h bluez-5.41/gdbus/gdbus.h --- bluez-5.37/gdbus/gdbus.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/gdbus/gdbus.h 2016-05-26 16:51:11.000000000 +0000 @@ -302,6 +302,15 @@ const char *name, const char *format, va_list args); void g_dbus_pending_property_error(GDBusPendingReply id, const char *name, const char *format, ...); + +/* + * Note that when multiple properties for a given object path are changed + * in the same mainloop iteration, they will be grouped with the last + * property changed. If this behaviour is undesired, use + * g_dbus_emit_property_changed_full() with the + * G_DBUS_PROPERTY_CHANGED_FLAG_FLUSH flag, causing the signal to ignore + * any grouping. + */ void g_dbus_emit_property_changed(DBusConnection *connection, const char *path, const char *interface, const char *name); diff -Nru bluez-5.37/gdbus/object.c bluez-5.41/gdbus/object.c --- bluez-5.37/gdbus/object.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/gdbus/object.c 2016-05-26 16:51:11.000000000 +0000 @@ -635,11 +635,19 @@ static void add_pending(struct generic_data *data) { - if (data->process_id > 0) - return; + guint old_id = data->process_id; data->process_id = g_idle_add(process_changes, data); + if (old_id > 0) { + /* + * If the element already had an old idler, remove the old one, + * no need to re-add it to the pending list. + */ + g_source_remove(old_id); + return; + } + pending = g_slist_append(pending, data); } diff -Nru bluez-5.37/lib/bluetooth.c bluez-5.41/lib/bluetooth.c --- bluez-5.37/lib/bluetooth.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/lib/bluetooth.c 2016-07-17 21:13:15.000000000 +0000 @@ -360,7 +360,7 @@ case 48: return "ST Microelectronics"; case 49: - return "Synopsis"; + return "Synopsys, Inc."; case 50: return "Red-M (Communications) Ltd"; case 51: @@ -548,7 +548,7 @@ case 142: return "Quintic Corp."; case 143: - return "Stollman E+V GmbH"; + return "Telit Wireless Solutions GmbH (Formerly Stollman E+V GmbH)"; case 144: return "Funai Electric Co., Ltd."; case 145: @@ -836,7 +836,7 @@ case 286: return "Skoda Auto a.s."; case 287: - return "Volkswagon AG"; + return "Volkswagen AG"; case 288: return "Porsche AG"; case 289: @@ -998,7 +998,7 @@ case 367: return "Podo Labs, Inc"; case 368: - return "Roche Diabetes Care AG"; + return "F. Hoffmann-La Roche AG"; case 369: return "Amazon Fulfillment Service"; case 370: @@ -1745,6 +1745,404 @@ return "Bytestorm Ltd."; case 741: return "Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )"; + case 742: + return "Unwire"; + case 743: + return "Connected Yard, Inc."; + case 744: + return "American Music Environments"; + case 745: + return "Sensogram Technologies, Inc."; + case 746: + return "Fujitsu Limited"; + case 747: + return "Ardic Technology"; + case 748: + return "Delta Systems, Inc"; + case 749: + return "HTC Corporation"; + case 750: + return "Citizen Holdings Co., Ltd."; + case 751: + return "SMART-INNOVATION.inc"; + case 752: + return "Blackrat Software"; + case 753: + return "The Idea Cave, LLC"; + case 754: + return "GoPro, Inc."; + case 755: + return "AuthAir, Inc"; + case 756: + return "Vensi, Inc."; + case 757: + return "Indagem Tech LLC"; + case 758: + return "Intemo Technologies"; + case 759: + return "DreamVisions co., Ltd."; + case 760: + return "Runteq Oy Ltd"; + case 761: + return "IMAGINATION TECHNOLOGIES LTD"; + case 762: + return "CoSTAR Technologies"; + case 763: + return "Clarius Mobile Health Corp."; + case 764: + return "Shanghai Frequen Microelectronics Co., Ltd."; + case 765: + return "Uwanna, Inc."; + case 766: + return "Lierda Science & Technology Group Co., Ltd."; + case 767: + return "Silicon Laboratories"; + case 768: + return "World Moto Inc."; + case 769: + return "Giatec Scientific Inc."; + case 770: + return "Loop Devices, Inc"; + case 771: + return "IACA electronique"; + case 772: + return "Martians Inc"; + case 773: + return "Swipp ApS"; + case 774: + return "Life Laboratory Inc."; + case 775: + return "FUJI INDUSTRIAL CO.,LTD."; + case 776: + return "Surefire, LLC"; + case 777: + return "Dolby Labs"; + case 778: + return "Ellisys"; + case 779: + return "Magnitude Lighting Converters"; + case 780: + return "Hilti AG"; + case 781: + return "Devdata S.r.l."; + case 782: + return "Deviceworx"; + case 783: + return "Shortcut Labs"; + case 784: + return "SGL Italia S.r.l."; + case 785: + return "PEEQ DATA"; + case 786: + return "Ducere Technologies Pvt Ltd"; + case 787: + return "DiveNav, Inc."; + case 788: + return "RIIG AI Sp. z o.o."; + case 789: + return "Thermo Fisher Scientific"; + case 790: + return "AG Measurematics Pvt. Ltd."; + case 791: + return "CHUO Electronics CO., LTD."; + case 792: + return "Aspenta International"; + case 793: + return "Eugster Frismag AG"; + case 794: + return "Amber wireless GmbH"; + case 795: + return "HQ Inc"; + case 796: + return "Lab Sensor Solutions"; + case 797: + return "Enterlab ApS"; + case 798: + return "Eyefi, Inc."; + case 799: + return "MetaSystem S.p.A"; + case 800: + return "SONO ELECTRONICS. CO., LTD"; + case 801: + return "Jewelbots"; + case 802: + return "Compumedics Limited"; + case 803: + return "Rotor Bike Components"; + case 804: + return "Astro, Inc."; + case 805: + return "Amotus Solutions"; + case 806: + return "Healthwear Technologies (Changzhou)Ltd"; + case 807: + return "Essex Electronics"; + case 808: + return "Grundfos A/S"; + case 809: + return "Eargo, Inc."; + case 810: + return "Electronic Design Lab"; + case 811: + return "ESYLUX"; + case 812: + return "NIPPON SMT.CO.,Ltd"; + case 813: + return "BM innovations GmbH"; + case 814: + return "indoormap"; + case 815: + return "OttoQ Inc"; + case 816: + return "North Pole Engineering"; + case 817: + return "3flares Technologies Inc."; + case 818: + return "Electrocompaniet A.S."; + case 819: + return "Mul-T-Lock"; + case 820: + return "Corentium AS"; + case 821: + return "Enlighted Inc"; + case 822: + return "GISTIC"; + case 823: + return "AJP2 Holdings, LLC"; + case 824: + return "COBI GmbH"; + case 825: + return "Blue Sky Scientific, LLC"; + case 826: + return "Appception, Inc."; + case 827: + return "Courtney Thorne Limited"; + case 828: + return "Virtuosys"; + case 829: + return "TPV Technology Limited"; + case 830: + return "Monitra SA"; + case 831: + return "Automation Components, Inc."; + case 832: + return "Letsense s.r.l."; + case 833: + return "Etesian Technologies LLC"; + case 834: + return "GERTEC BRASIL LTDA."; + case 835: + return "Drekker Development Pty. Ltd."; + case 836: + return "Whirl Inc"; + case 837: + return "Locus Positioning"; + case 838: + return "Acuity Brands Lighting, Inc"; + case 839: + return "Prevent Biometrics"; + case 840: + return "Arioneo"; + case 841: + return "VersaMe"; + case 842: + return "Vaddio"; + case 843: + return "Libratone A/S"; + case 844: + return "HM Electronics, Inc."; + case 845: + return "TASER International, Inc."; + case 846: + return "Safe Trust Inc."; + case 847: + return "Heartland Payment Systems"; + case 848: + return "Bitstrata Systems Inc."; + case 849: + return "Pieps GmbH"; + case 850: + return "iRiding(Xiamen)Technology Co.,Ltd."; + case 851: + return "Alpha Audiotronics, Inc."; + case 852: + return "TOPPAN FORMS CO.,LTD."; + case 853: + return "Sigma Designs, Inc."; + case 854: + return "Spectrum Brands, Inc."; + case 855: + return "Polymap Wireless"; + case 856: + return "MagniWare Ltd."; + case 857: + return "Novotec Medical GmbH"; + case 858: + return "Medicom Innovation Partner a/s"; + case 859: + return "Matrix Inc."; + case 860: + return "Eaton Corporation"; + case 861: + return "KYS"; + case 862: + return "Naya Health, Inc."; + case 863: + return "Acromag"; + case 864: + return "Insulet Corporation"; + case 865: + return "Wellinks Inc."; + case 866: + return "ON Semiconductor"; + case 867: + return "FREELAP SA"; + case 868: + return "Favero Electronics Srl"; + case 869: + return "BioMech Sensor LLC"; + case 870: + return "BOLTT Sports technologies Private limited"; + case 871: + return "Saphe International"; + case 872: + return "Metormote AB"; + case 873: + return "littleBits"; + case 874: + return "SetPoint Medical"; + case 875: + return "BRControls Products BV"; + case 876: + return "Zipcar"; + case 877: + return "AirBolt Pty Ltd"; + case 878: + return "KeepTruckin Inc"; + case 879: + return "Motiv, Inc."; + case 880: + return "Wazombi Labs OÜ"; + case 881: + return "ORBCOMM"; + case 882: + return "Nixie Labs, Inc."; + case 883: + return "AppNearMe Ltd"; + case 884: + return "Holman Industries"; + case 885: + return "Expain AS"; + case 886: + return "Electronic Temperature Instruments Ltd"; + case 887: + return "Plejd AB"; + case 888: + return "Propeller Health"; + case 889: + return "Shenzhen iMCO Electronic Technology Co.,Ltd"; + case 890: + return "Algoria"; + case 891: + return "Apption Labs Inc."; + case 892: + return "Cronologics Corporation"; + case 893: + return "MICRODIA Ltd."; + case 894: + return "lulabytes S.L."; + case 895: + return "Nestec S.A."; + case 896: + return "LLC \"MEGA-F service\""; + case 897: + return "Sharp Corporation"; + case 898: + return "Precision Outcomes Ltd"; + case 899: + return "Kronos Incorporated"; + case 900: + return "OCOSMOS Co., Ltd."; + case 901: + return "Embedded Electronic Solutions Ltd. dba e2Solutions"; + case 902: + return "Aterica Inc."; + case 903: + return "BluStor PMC, Inc."; + case 904: + return "Kapsch TrafficCom AB"; + case 905: + return "ActiveBlu Corporation"; + case 906: + return "Kohler Mira Limited"; + case 907: + return "Noke"; + case 908: + return "Appion Inc."; + case 909: + return "Resmed Ltd"; + case 910: + return "Crownstone B.V."; + case 911: + return "Xiaomi Inc."; + case 912: + return "INFOTECH s.r.o."; + case 913: + return "Thingsquare AB"; + case 914: + return "T&D"; + case 915: + return "LAVAZZA S.p.A."; + case 916: + return "Netclearance Systems, Inc."; + case 917: + return "SDATAWAY"; + case 918: + return "BLOKS GmbH"; + case 919: + return "LEGO System A/S"; + case 920: + return "Thetatronics Ltd"; + case 921: + return "Nikon Corporation"; + case 922: + return "NeST"; + case 923: + return "South Silicon Valley Microelectronics"; + case 924: + return "ALE International"; + case 925: + return "CareView Communications, Inc."; + case 926: + return "SchoolBoard Limited"; + case 927: + return "Molex Corporation"; + case 928: + return "IVT Wireless Limited"; + case 929: + return "Alpine Labs LLC"; + case 930: + return "Candura Instruments"; + case 931: + return "SmartMovt Technology Co., Ltd"; + case 932: + return "Token Zero Ltd"; + case 933: + return "ACE CAD Enterprise Co., Ltd. (ACECAD)"; + case 934: + return "Medela, Inc"; + case 935: + return "AeroScout"; + case 936: + return "Esrille Inc."; + case 937: + return "THINKERLY SRL"; + case 938: + return "Exon Sp. z o.o."; + case 939: + return "Meizu Technology Co., Ltd."; + case 940: + return "Smablo LTD"; case 65535: return "internal use"; default: diff -Nru bluez-5.37/lib/bluetooth.h bluez-5.41/lib/bluetooth.h --- bluez-5.37/lib/bluetooth.h 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/lib/bluetooth.h 2016-05-26 16:51:11.000000000 +0000 @@ -69,6 +69,7 @@ #define BT_SECURITY_LOW 1 #define BT_SECURITY_MEDIUM 2 #define BT_SECURITY_HIGH 3 +#define BT_SECURITY_FIPS 4 #define BT_DEFER_SETUP 7 diff -Nru bluez-5.37/lib/hci.c bluez-5.41/lib/hci.c --- bluez-5.37/lib/hci.c 2014-12-26 17:54:42.000000000 +0000 +++ bluez-5.41/lib/hci.c 2016-07-17 21:13:15.000000000 +0000 @@ -143,7 +143,7 @@ { switch (bus) { case HCI_VIRTUAL: - return "VIRTUAL"; + return "Virtual"; case HCI_USB: return "USB"; case HCI_PCCARD: @@ -156,8 +156,12 @@ return "PCI"; case HCI_SDIO: return "SDIO"; + case HCI_SPI: + return "SPI"; + case HCI_I2C: + return "I2C"; default: - return "UNKNOWN"; + return "Unknown"; } } @@ -169,12 +173,12 @@ char *hci_typetostr(int type) { switch (type) { - case HCI_BREDR: - return "BR/EDR"; + case HCI_PRIMARY: + return "Primary"; case HCI_AMP: return "AMP"; default: - return "UNKNOWN"; + return "Unknown"; } } diff -Nru bluez-5.37/lib/hci.h bluez-5.41/lib/hci.h --- bluez-5.37/lib/hci.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/lib/hci.h 2016-07-17 21:13:15.000000000 +0000 @@ -55,10 +55,13 @@ #define HCI_RS232 4 #define HCI_PCI 5 #define HCI_SDIO 6 +#define HCI_SPI 7 +#define HCI_I2C 8 /* HCI controller types */ -#define HCI_BREDR 0x00 +#define HCI_PRIMARY 0x00 #define HCI_AMP 0x01 +#define HCI_BREDR HCI_PRIMARY /* HCI device flags */ enum { diff -Nru bluez-5.37/lib/mgmt.h bluez-5.41/lib/mgmt.h --- bluez-5.37/lib/mgmt.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/lib/mgmt.h 2016-03-18 02:18:03.000000000 +0000 @@ -528,6 +528,8 @@ uint8_t max_scan_rsp_len; } __packed; +#define MGMT_OP_START_LIMITED_DISCOVERY 0x0041 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { uint16_t opcode; diff -Nru bluez-5.37/lib/uuid.c bluez-5.41/lib/uuid.c --- bluez-5.37/lib/uuid.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/lib/uuid.c 2016-03-18 02:18:03.000000000 +0000 @@ -183,14 +183,14 @@ static inline int is_base_uuid128(const char *string) { uint16_t uuid; - char dummy; + char dummy[2]; if (!is_uuid128(string)) return 0; return sscanf(string, "0000%04hx-0000-1000-8000-00805%1[fF]9%1[bB]34%1[fF]%1[bB]", - &uuid, &dummy, &dummy, &dummy, &dummy) == 5; + &uuid, dummy, dummy, dummy, dummy) == 5; } static inline int is_uuid32(const char *string) diff -Nru bluez-5.37/Makefile.am bluez-5.41/Makefile.am --- bluez-5.37/Makefile.am 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/Makefile.am 2016-07-17 21:13:15.000000000 +0000 @@ -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:10:18 +lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:13:18 lib_libbluetooth_la_DEPENDENCIES = $(local_headers) endif @@ -118,7 +118,8 @@ src/shared/gatt-client.h src/shared/gatt-client.c \ src/shared/gatt-server.h src/shared/gatt-server.c \ src/shared/gatt-db.h src/shared/gatt-db.c \ - src/shared/gap.h src/shared/gap.c + src/shared/gap.h src/shared/gap.c \ + src/shared/tty.h src_libshared_glib_la_SOURCES = $(shared_sources) \ src/shared/io-glib.c \ @@ -261,6 +262,8 @@ EXTRA_DIST += doc/pics-opp.txt doc/pixit-opp.txt \ doc/pts-opp.txt +EXTRA_DIST += doc/btsnoop.txt + EXTRA_DIST += tools/magic.btsnoop AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ diff -Nru bluez-5.37/Makefile.in bluez-5.41/Makefile.in --- bluez-5.37/Makefile.in 2015-12-28 02:14:13.000000000 +0000 +++ bluez-5.41/Makefile.in 2016-07-17 21:14:12.000000000 +0000 @@ -116,24 +116,24 @@ @EXPERIMENTAL_TRUE@ profiles/health/hdp.h profiles/health/hdp.c \ @EXPERIMENTAL_TRUE@ profiles/health/hdp_util.h profiles/health/hdp_util.c -@EXPERIMENTAL_TRUE@am__append_10 = alert time proximity thermometer \ -@EXPERIMENTAL_TRUE@ heartrate cyclingspeed -@EXPERIMENTAL_TRUE@am__append_11 = profiles/alert/server.c \ -@EXPERIMENTAL_TRUE@ profiles/time/server.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/main.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/manager.h \ -@EXPERIMENTAL_TRUE@ profiles/proximity/manager.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/monitor.h \ -@EXPERIMENTAL_TRUE@ profiles/proximity/monitor.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/reporter.h \ -@EXPERIMENTAL_TRUE@ profiles/proximity/reporter.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/linkloss.h \ -@EXPERIMENTAL_TRUE@ profiles/proximity/linkloss.c \ -@EXPERIMENTAL_TRUE@ profiles/proximity/immalert.h \ -@EXPERIMENTAL_TRUE@ profiles/proximity/immalert.c \ -@EXPERIMENTAL_TRUE@ profiles/thermometer/thermometer.c \ -@EXPERIMENTAL_TRUE@ profiles/heartrate/heartrate.c \ -@EXPERIMENTAL_TRUE@ profiles/cyclingspeed/cyclingspeed.c +@DEPRECATED_TRUE@am__append_10 = alert time proximity thermometer \ +@DEPRECATED_TRUE@ heartrate cyclingspeed +@DEPRECATED_TRUE@am__append_11 = profiles/alert/server.c \ +@DEPRECATED_TRUE@ profiles/time/server.c \ +@DEPRECATED_TRUE@ profiles/proximity/main.c \ +@DEPRECATED_TRUE@ profiles/proximity/manager.h \ +@DEPRECATED_TRUE@ profiles/proximity/manager.c \ +@DEPRECATED_TRUE@ profiles/proximity/monitor.h \ +@DEPRECATED_TRUE@ profiles/proximity/monitor.c \ +@DEPRECATED_TRUE@ profiles/proximity/reporter.h \ +@DEPRECATED_TRUE@ profiles/proximity/reporter.c \ +@DEPRECATED_TRUE@ profiles/proximity/linkloss.h \ +@DEPRECATED_TRUE@ profiles/proximity/linkloss.c \ +@DEPRECATED_TRUE@ profiles/proximity/immalert.h \ +@DEPRECATED_TRUE@ profiles/proximity/immalert.c \ +@DEPRECATED_TRUE@ profiles/thermometer/thermometer.c \ +@DEPRECATED_TRUE@ profiles/heartrate/heartrate.c \ +@DEPRECATED_TRUE@ profiles/cyclingspeed/cyclingspeed.c @SIXAXIS_TRUE@am__append_12 = plugins/sixaxis.la @MAINTAINER_MODE_TRUE@am__append_13 = plugins/external-dummy.la @CLIENT_TRUE@am__append_14 = client/bluetoothctl @@ -757,7 +757,7 @@ monitor/bnep.c monitor/uuid.h monitor/uuid.c monitor/hwdb.h \ monitor/hwdb.c monitor/keys.h monitor/keys.c monitor/analyze.h \ monitor/analyze.c monitor/intel.h monitor/intel.c \ - monitor/broadcom.h monitor/broadcom.c + monitor/broadcom.h monitor/broadcom.c monitor/tty.h @MONITOR_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \ @MONITOR_TRUE@ monitor/display.$(OBJEXT) \ @MONITOR_TRUE@ monitor/hcidump.$(OBJEXT) \ @@ -974,18 +974,18 @@ @EXPERIMENTAL_TRUE@ profiles/health/bluetoothd-hdp_manager.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ profiles/health/bluetoothd-hdp.$(OBJEXT) \ @EXPERIMENTAL_TRUE@ profiles/health/bluetoothd-hdp_util.$(OBJEXT) -@EXPERIMENTAL_TRUE@am__objects_14 = \ -@EXPERIMENTAL_TRUE@ profiles/alert/bluetoothd-server.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/time/bluetoothd-server.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-main.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-manager.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-monitor.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-reporter.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-linkloss.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/proximity/bluetoothd-immalert.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/thermometer/bluetoothd-thermometer.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/heartrate/bluetoothd-heartrate.$(OBJEXT) \ -@EXPERIMENTAL_TRUE@ profiles/cyclingspeed/bluetoothd-cyclingspeed.$(OBJEXT) +@DEPRECATED_TRUE@am__objects_14 = \ +@DEPRECATED_TRUE@ profiles/alert/bluetoothd-server.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/time/bluetoothd-server.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-main.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-manager.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-monitor.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-reporter.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-linkloss.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/proximity/bluetoothd-immalert.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/thermometer/bluetoothd-thermometer.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/heartrate/bluetoothd-heartrate.$(OBJEXT) \ +@DEPRECATED_TRUE@ profiles/cyclingspeed/bluetoothd-cyclingspeed.$(OBJEXT) am__objects_15 = plugins/bluetoothd-hostname.$(OBJEXT) \ plugins/bluetoothd-wiimote.$(OBJEXT) \ plugins/bluetoothd-autopair.$(OBJEXT) \ @@ -2236,7 +2236,8 @@ doc/thermometer-api.txt doc/cyclingspeed-api.txt \ doc/gatt-api.txt doc/advertising-api.txt doc/obex-api.txt \ doc/obex-agent-api.txt doc/pics-opp.txt doc/pixit-opp.txt \ - doc/pts-opp.txt tools/magic.btsnoop $(manual_pages:.1=.txt) + doc/pts-opp.txt doc/btsnoop.txt tools/magic.btsnoop \ + $(manual_pages:.1=.txt) include_HEADERS = $(am__append_1) AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS) $(am__append_32) \ @DBUS_CFLAGS@ @GLIB_CFLAGS@ $(am__empty) @@ -2266,7 +2267,7 @@ local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) BUILT_SOURCES = $(local_headers) src/builtin.h obexd/src/builtin.h @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:10:18 +@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:13:18 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers) lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) @@ -2296,7 +2297,8 @@ src/shared/gatt-client.h src/shared/gatt-client.c \ src/shared/gatt-server.h src/shared/gatt-server.c \ src/shared/gatt-db.h src/shared/gatt-db.c \ - src/shared/gap.h src/shared/gap.c + src/shared/gap.h src/shared/gap.c \ + src/shared/tty.h src_libshared_glib_la_SOURCES = $(shared_sources) \ src/shared/io-glib.c \ @@ -2465,7 +2467,8 @@ @MONITOR_TRUE@ monitor/keys.h monitor/keys.c \ @MONITOR_TRUE@ monitor/analyze.h monitor/analyze.c \ @MONITOR_TRUE@ monitor/intel.h monitor/intel.c \ -@MONITOR_TRUE@ monitor/broadcom.h monitor/broadcom.c +@MONITOR_TRUE@ monitor/broadcom.h monitor/broadcom.c \ +@MONITOR_TRUE@ monitor/tty.h @MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la \ @MONITOR_TRUE@ src/libshared-mainloop.la @UDEV_LIBS@ diff -Nru bluez-5.37/Makefile.plugins bluez-5.41/Makefile.plugins --- bluez-5.37/Makefile.plugins 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/Makefile.plugins 2016-04-04 22:21:51.000000000 +0000 @@ -87,7 +87,7 @@ builtin_modules += deviceinfo builtin_sources += profiles/deviceinfo/deviceinfo.c -if EXPERIMENTAL +if DEPRECATED builtin_modules += alert builtin_sources += profiles/alert/server.c diff -Nru bluez-5.37/Makefile.tools bluez-5.41/Makefile.tools --- bluez-5.37/Makefile.tools 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/Makefile.tools 2016-05-26 16:51:11.000000000 +0000 @@ -36,7 +36,8 @@ monitor/keys.h monitor/keys.c \ monitor/analyze.h monitor/analyze.c \ monitor/intel.h monitor/intel.c \ - monitor/broadcom.h monitor/broadcom.c + monitor/broadcom.h monitor/broadcom.c \ + monitor/tty.h monitor_btmon_LDADD = lib/libbluetooth-internal.la \ src/libshared-mainloop.la @UDEV_LIBS@ endif diff -Nru bluez-5.37/monitor/analyze.c bluez-5.41/monitor/analyze.c --- bluez-5.37/monitor/analyze.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/analyze.c 2016-07-17 21:13:15.000000000 +0000 @@ -29,6 +29,8 @@ #include #include +#include "lib/bluetooth.h" + #include "src/shared/util.h" #include "src/shared/queue.h" #include "src/shared/btsnoop.h" @@ -45,6 +47,11 @@ unsigned long num_evt; unsigned long num_acl; unsigned long num_sco; + unsigned long vendor_diag; + unsigned long system_note; + unsigned long user_log; + unsigned long unknown; + uint16_t manufacturer; }; static struct queue *dev_list; @@ -67,13 +74,22 @@ } printf("Found %s controller with index %u\n", str, dev->index); - printf(" BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n", + printf(" BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3], dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]); + if (dev->manufacturer != 0xffff) + printf(" (%s)", bt_compidtostr(dev->manufacturer)); + printf("\n"); + + printf(" %lu commands\n", dev->num_cmd); printf(" %lu events\n", dev->num_evt); printf(" %lu ACL packets\n", dev->num_acl); printf(" %lu SCO packets\n", dev->num_sco); + printf(" %lu vendor diagnostics\n", dev->vendor_diag); + printf(" %lu system notes\n", dev->system_note); + printf(" %lu user logs\n", dev->user_log); + printf(" %lu unknown opcodes\n", dev->unknown); printf("\n"); free(dev); @@ -84,12 +100,9 @@ struct hci_dev *dev; dev = new0(struct hci_dev, 1); - if (!dev) { - fprintf(stderr, "Failed to allocate new device entry\n"); - return NULL; - } dev->index = index; + dev->manufacturer = 0xffff; return dev; } @@ -111,8 +124,6 @@ fprintf(stderr, "Creating new device for unknown index\n"); dev = dev_alloc(index); - if (!dev) - return NULL; queue_push_tail(dev_list, dev); } @@ -127,8 +138,6 @@ struct hci_dev *dev; dev = dev_alloc(index); - if (!dev) - return; dev->type = ni->type; memcpy(dev->bdaddr, ni->bdaddr, 6); @@ -251,6 +260,70 @@ dev->num_sco++; } +static void info_index(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + const struct btsnoop_opcode_index_info *hdr = data; + struct hci_dev *dev; + + data += sizeof(*hdr); + size -= sizeof(*hdr); + + dev = dev_lookup(index); + if (!dev) + return; + + dev->manufacturer = hdr->manufacturer; +} + +static void vendor_diag(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + struct hci_dev *dev; + + dev = dev_lookup(index); + if (!dev) + return; + + dev->vendor_diag++; +} + +static void system_note(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + struct hci_dev *dev; + + dev = dev_lookup(index); + if (!dev) + return; + + dev->system_note++; +} + +static void user_log(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + struct hci_dev *dev; + + dev = dev_lookup(index); + if (!dev) + return; + + dev->user_log++; +} + +static void unknown_opcode(struct timeval *tv, uint16_t index, + const void *data, uint16_t size) +{ + struct hci_dev *dev; + + dev = dev_lookup(index); + if (!dev) + return; + + dev->unknown++; +} + void analyze_trace(const char *path) { struct btsnoop *btsnoop_file; @@ -274,10 +347,6 @@ } dev_list = queue_new(); - if (!dev_list) { - fprintf(stderr, "Failed to allocate device list\n"); - goto done; - } while (1) { unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; @@ -312,9 +381,22 @@ case BTSNOOP_OPCODE_OPEN_INDEX: case BTSNOOP_OPCODE_CLOSE_INDEX: break; + case BTSNOOP_OPCODE_INDEX_INFO: + info_index(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_VENDOR_DIAG: + vendor_diag(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_SYSTEM_NOTE: + system_note(&tv, index, buf, pktlen); + break; + case BTSNOOP_OPCODE_USER_LOGGING: + user_log(&tv, index, buf, pktlen); + break; default: - fprintf(stderr, "Wrong opcode %u\n", opcode); - goto done; + fprintf(stderr, "Unknown opcode %u\n", opcode); + unknown_opcode(&tv, index, buf, pktlen); + break; } num_packets++; diff -Nru bluez-5.37/monitor/avctp.c bluez-5.41/monitor/avctp.c --- bluez-5.37/monitor/avctp.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/avctp.c 2016-03-18 02:18:03.000000000 +0000 @@ -2430,7 +2430,12 @@ if (!l2cap_frame_get_u8(frame, &len)) return false; - printf("Folder: "); + if (!len) { + print_field("%*cFolder: ", indent, ' '); + continue; + } + + printf("%*cFolder: ", indent+8, ' '); for (; len > 0; len--) { uint8_t c; diff -Nru bluez-5.37/monitor/control.c bluez-5.41/monitor/control.c --- bluez-5.37/monitor/control.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/control.c 2016-05-26 16:51:11.000000000 +0000 @@ -35,6 +35,10 @@ #include #include #include +#include +#include +#include +#include #include "lib/bluetooth.h" #include "lib/hci.h" @@ -48,6 +52,7 @@ #include "packet.h" #include "hcidump.h" #include "ellisys.h" +#include "tty.h" #include "control.h" static struct btsnoop *btsnoop_file = NULL; @@ -965,7 +970,7 @@ data->buf, pktlen); break; case HCI_CHANNEL_MONITOR: - btsnoop_write_hci(btsnoop_file, tv, index, opcode, + btsnoop_write_hci(btsnoop_file, tv, index, opcode, 0, data->buf, pktlen); ellisys_inject_hci(tv, index, opcode, data->buf, pktlen); @@ -1058,23 +1063,25 @@ data->offset += len; - if (data->offset > MGMT_HDR_SIZE) { + while (data->offset >= MGMT_HDR_SIZE) { struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf; uint16_t pktlen = le16_to_cpu(hdr->len); + uint16_t opcode, index; - if (data->offset > pktlen + MGMT_HDR_SIZE) { - uint16_t opcode = le16_to_cpu(hdr->opcode); - uint16_t index = le16_to_cpu(hdr->index); + if (data->offset < pktlen + MGMT_HDR_SIZE) + return; - packet_monitor(NULL, NULL, index, opcode, + opcode = le16_to_cpu(hdr->opcode); + index = le16_to_cpu(hdr->index); + + packet_monitor(NULL, NULL, index, opcode, data->buf + MGMT_HDR_SIZE, pktlen); - data->offset -= pktlen + MGMT_HDR_SIZE; + data->offset -= pktlen + MGMT_HDR_SIZE; - if (data->offset > 0) - memmove(data->buf, data->buf + - MGMT_HDR_SIZE + pktlen, data->offset); - } + if (data->offset > 0) + memmove(data->buf, data->buf + MGMT_HDR_SIZE + pktlen, + data->offset); } } @@ -1157,6 +1164,202 @@ server_fd = fd; } +static bool parse_drops(uint8_t **data, uint8_t *len, uint8_t *drops, + uint32_t *total) +{ + if (*len < 1) + return false; + + *drops = **data; + *total += *drops; + (*data)++; + (*len)--; + + return true; +} + +static bool tty_parse_header(uint8_t *hdr, uint8_t len, struct timeval **tv, + struct timeval *ctv, uint32_t *drops) +{ + uint8_t cmd = 0; + uint8_t evt = 0; + uint8_t acl_tx = 0; + uint8_t acl_rx = 0; + uint8_t sco_tx = 0; + uint8_t sco_rx = 0; + uint8_t other = 0; + uint32_t total = 0; + uint32_t ts32; + + while (len) { + uint8_t type = hdr[0]; + + hdr++; len--; + + switch (type) { + case TTY_EXTHDR_COMMAND_DROPS: + if (!parse_drops(&hdr, &len, &cmd, &total)) + return false; + break; + case TTY_EXTHDR_EVENT_DROPS: + if (!parse_drops(&hdr, &len, &evt, &total)) + return false; + break; + case TTY_EXTHDR_ACL_TX_DROPS: + if (!parse_drops(&hdr, &len, &acl_tx, &total)) + return false; + break; + case TTY_EXTHDR_ACL_RX_DROPS: + if (!parse_drops(&hdr, &len, &acl_rx, &total)) + return false; + break; + case TTY_EXTHDR_SCO_TX_DROPS: + if (!parse_drops(&hdr, &len, &sco_tx, &total)) + return false; + break; + case TTY_EXTHDR_SCO_RX_DROPS: + if (!parse_drops(&hdr, &len, &sco_rx, &total)) + return false; + break; + case TTY_EXTHDR_OTHER_DROPS: + if (!parse_drops(&hdr, &len, &other, &total)) + return false; + break; + case TTY_EXTHDR_TS32: + if (len < sizeof(ts32)) + return false; + ts32 = get_le32(hdr); + hdr += sizeof(ts32); len -= sizeof(ts32); + /* ts32 is in units of 1/10th of a millisecond */ + ctv->tv_sec = ts32 / 10000; + ctv->tv_usec = (ts32 % 10000) * 100; + *tv = ctv; + break; + default: + printf("Unknown extended header type %u\n", type); + return false; + } + } + + if (total) { + *drops += total; + printf("* Drops: cmd %u evt %u acl_tx %u acl_rx %u sco_tx %u " + "sco_rx %u other %u\n", cmd, evt, acl_tx, acl_rx, + sco_tx, sco_rx, other); + } + + return true; +} + +static void tty_callback(int fd, uint32_t events, void *user_data) +{ + struct control_data *data = user_data; + ssize_t len; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_remove_fd(data->fd); + return; + } + + len = read(data->fd, data->buf + data->offset, + sizeof(data->buf) - data->offset); + if (len < 0) + return; + + data->offset += len; + + while (data->offset >= sizeof(struct tty_hdr)) { + struct tty_hdr *hdr = (struct tty_hdr *) data->buf; + uint16_t pktlen, opcode, data_len; + struct timeval *tv = NULL; + struct timeval ctv; + uint32_t drops = 0; + + data_len = le16_to_cpu(hdr->data_len); + + if (data->offset < 2 + data_len) + return; + + if (data->offset < sizeof(*hdr) + hdr->hdr_len) { + fprintf(stderr, "Received corrupted data from TTY\n"); + memmove(data->buf, data->buf + 2 + data_len, + data->offset); + return; + } + + if (!tty_parse_header(hdr->ext_hdr, hdr->hdr_len, + &tv, &ctv, &drops)) + fprintf(stderr, "Unable to parse extended header\n"); + + opcode = le16_to_cpu(hdr->opcode); + pktlen = data_len - 4 - hdr->hdr_len; + + btsnoop_write_hci(btsnoop_file, tv, 0, opcode, drops, + hdr->ext_hdr + hdr->hdr_len, pktlen); + packet_monitor(tv, NULL, 0, opcode, + hdr->ext_hdr + hdr->hdr_len, pktlen); + + data->offset -= 2 + data_len; + + if (data->offset > 0) + memmove(data->buf, data->buf + 2 + data_len, + data->offset); + } +} + +int control_tty(const char *path, unsigned int speed) +{ + struct control_data *data; + struct termios ti; + int fd, err; + + fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) { + err = -errno; + perror("Failed to open serial port"); + return err; + } + + if (tcflush(fd, TCIOFLUSH) < 0) { + err = -errno; + perror("Failed to flush serial port"); + close(fd); + return err; + } + + memset(&ti, 0, sizeof(ti)); + /* Switch TTY to raw mode */ + cfmakeraw(&ti); + + ti.c_cflag |= (CLOCAL | CREAD); + ti.c_cflag &= ~CRTSCTS; + + cfsetspeed(&ti, speed); + + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + err = -errno; + perror("Failed to set serial port settings"); + close(fd); + return err; + } + + printf("--- %s opened ---\n", path); + + data = malloc(sizeof(*data)); + if (!data) { + close(fd); + return -ENOMEM; + } + + memset(data, 0, sizeof(*data)); + data->channel = HCI_CHANNEL_MONITOR; + data->fd = fd; + + mainloop_add_fd(data->fd, EPOLLIN, tty_callback, data, free_data); + + return 0; +} + bool control_writer(const char *path) { btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR); diff -Nru bluez-5.37/monitor/control.h bluez-5.41/monitor/control.h --- bluez-5.37/monitor/control.h 2014-08-06 17:25:36.000000000 +0000 +++ bluez-5.41/monitor/control.h 2016-05-26 16:51:11.000000000 +0000 @@ -27,6 +27,7 @@ bool control_writer(const char *path); void control_reader(const char *path); void control_server(const char *path); +int control_tty(const char *path, unsigned int speed); int control_tracing(void); void control_message(uint16_t opcode, const void *data, uint16_t size); diff -Nru bluez-5.37/monitor/l2cap.c bluez-5.41/monitor/l2cap.c --- bluez-5.37/monitor/l2cap.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/l2cap.c 2016-07-17 21:13:15.000000000 +0000 @@ -1999,6 +1999,7 @@ static void print_uuid(const char *label, const void *data, uint16_t size) { const char *str; + char uuidstr[36]; switch (size) { case 2: @@ -2010,12 +2011,12 @@ print_field("%s: %s (0x%8.8x)", label, str, get_le32(data)); break; case 16: - str = uuid128_to_str(data); - print_field("%s: %s (%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x)", - label, str, + sprintf(uuidstr, "%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", get_le32(data + 12), get_le16(data + 10), get_le16(data + 8), get_le16(data + 6), get_le32(data + 2), get_le16(data + 0)); + str = uuidstr_to_str(uuidstr); + print_field("%s: %s (%s)", label, str, uuidstr); break; default: packet_hexdump(data, size); diff -Nru bluez-5.37/monitor/main.c bluez-5.41/monitor/main.c --- bluez-5.37/monitor/main.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/main.c 2016-05-26 16:51:11.000000000 +0000 @@ -33,6 +33,7 @@ #include #include "src/shared/mainloop.h" +#include "src/shared/tty.h" #include "packet.h" #include "lmp.h" @@ -63,6 +64,8 @@ "\t-s, --server Start monitor server socket\n" "\t-p, --priority Show only priority or lower\n" "\t-i, --index Show only specified controller\n" + "\t-d, --tty Read data from TTY\n" + "\t-B, --tty-speed Set TTY speed (default 115200)\n" "\t-t, --time Show time instead of time offset\n" "\t-T, --date Show time and date information\n" "\t-S, --sco Dump SCO traffic\n" @@ -71,6 +74,8 @@ } static const struct option main_options[] = { + { "tty", required_argument, NULL, 'd' }, + { "tty-speed", required_argument, NULL, 'B' }, { "read", required_argument, NULL, 'r' }, { "write", required_argument, NULL, 'w' }, { "analyze", required_argument, NULL, 'a' }, @@ -94,6 +99,8 @@ const char *writer_path = NULL; const char *analyze_path = NULL; const char *ellisys_server = NULL; + const char *tty = NULL; + unsigned int tty_speed = B115200; unsigned short ellisys_port = 0; const char *str; int exit_status; @@ -106,12 +113,22 @@ for (;;) { int opt; - opt = getopt_long(argc, argv, "r:w:a:s:p:i:tTSE:vh", + opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh", main_options, NULL); if (opt < 0) break; switch (opt) { + case 'd': + tty= optarg; + break; + case 'B': + tty_speed = tty_get_speed(atoi(optarg)); + if (!tty_speed) { + fprintf(stderr, "Unknown speed: %s\n", optarg); + return EXIT_FAILURE; + } + break; case 'r': reader_path = optarg; break; @@ -212,7 +229,10 @@ if (ellisys_server) ellisys_enable(ellisys_server, ellisys_port); - if (control_tracing() < 0) + if (!tty && control_tracing() < 0) + return EXIT_FAILURE; + + if (tty && control_tty(tty, tty_speed) < 0) return EXIT_FAILURE; exit_status = mainloop_run(); diff -Nru bluez-5.37/monitor/packet.c bluez-5.41/monitor/packet.c --- bluez-5.37/monitor/packet.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/packet.c 2016-07-17 21:13:15.000000000 +0000 @@ -1870,7 +1870,9 @@ static void print_pk256(const char *label, const uint8_t *key) { - print_hex_field(label, key, 64); + print_field("%s:", label); + print_hex_field(" X", &key[0], 32); + print_hex_field(" Y", &key[32], 32); } static void print_dhkey(const uint8_t *dhkey) @@ -5540,7 +5542,7 @@ if (index_current < MAX_INDEX) { switch (index_list[index_current].type) { - case HCI_BREDR: + case HCI_PRIMARY: print_lmp_version(rsp->lmp_ver, rsp->lmp_subver); break; case HCI_AMP: diff -Nru bluez-5.37/monitor/tty.h bluez-5.41/monitor/tty.h --- bluez-5.37/monitor/tty.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.41/monitor/tty.h 2016-05-26 16:51:11.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2016 Intel Corporation + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +struct tty_hdr { + uint16_t data_len; + uint16_t opcode; + uint8_t flags; + uint8_t hdr_len; + uint8_t ext_hdr[0]; +} __attribute__ ((packed)); + +#define TTY_EXTHDR_COMMAND_DROPS 1 +#define TTY_EXTHDR_EVENT_DROPS 2 +#define TTY_EXTHDR_ACL_TX_DROPS 3 +#define TTY_EXTHDR_ACL_RX_DROPS 4 +#define TTY_EXTHDR_SCO_TX_DROPS 5 +#define TTY_EXTHDR_SCO_RX_DROPS 6 +#define TTY_EXTHDR_OTHER_DROPS 7 +#define TTY_EXTHDR_TS32 8 diff -Nru bluez-5.37/monitor/uuid.c bluez-5.41/monitor/uuid.c --- bluez-5.37/monitor/uuid.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/monitor/uuid.c 2016-07-17 21:13:15.000000000 +0000 @@ -31,7 +31,7 @@ #include "uuid.h" -static struct { +static const struct { uint16_t uuid; const char *str; } uuid16_table[] = { @@ -513,12 +513,58 @@ { 0xfe7d, "Aterica Health Inc." }, { 0xfe7c, "Stollmann E+V GmbH" }, { 0xfe7b, "Orion Labs, Inc." }, + { 0xfe7a, "Bragi GmbH" }, + { 0xfe79, "Zebra Technologies" }, + { 0xfe78, "Hewlett-Packard Company" }, + { 0xfe77, "Hewlett-Packard Company" }, + { 0xfe76, "TangoMe" }, + { 0xfe75, "TangoMe" }, + { 0xfe74, "unwire" }, + { 0xfe73, "St. Jude Medical, Inc." }, + { 0xfe72, "St. Jude Medical, Inc." }, + { 0xfe71, "Plume Design Inc" }, + { 0xfe70, "Beijing Jingdong Century Trading Co., Ltd." }, + { 0xfe6f, "LINE Corporation" }, + { 0xfe6e, "The University of Tokyo" }, + { 0xfe6d, "The University of Tokyo" }, + { 0xfe6c, "TASER International, Inc." }, + { 0xfe6b, "TASER International, Inc." }, + { 0xfe6a, "Kontakt Micro-Location Sp. z o.o." }, + { 0xfe69, "Qualcomm Life Inc" }, + { 0xfe68, "Qualcomm Life Inc" }, + { 0xfe67, "Lab Sensor Solutions" }, + { 0xfe66, "Intel Corporation" }, /* SDO defined */ { 0xfffe, "Alliance for Wireless Power (A4WP)" }, { 0xfffd, "Fast IDentity Online Alliance (FIDO)" }, { } }; +static const struct { + const char *uuid; + const char *str; +} uuid128_table[] = { + { "a3c87500-8ed3-4bdf-8a39-a01bebede295", + "Eddystone Configuration Service" }, + { "a3c87501-8ed3-4bdf-8a39-a01bebede295", "Capabilities" }, + { "a3c87502-8ed3-4bdf-8a39-a01bebede295", "Active Slot" }, + { "a3c87503-8ed3-4bdf-8a39-a01bebede295", + "Advertising Interval" }, + { "a3c87504-8ed3-4bdf-8a39-a01bebede295", "Radio Tx Power" }, + { "a3c87505-8ed3-4bdf-8a39-a01bebede295", + "(Advanced) Advertised Tx Power" }, + { "a3c87506-8ed3-4bdf-8a39-a01bebede295", "Lock State" }, + { "a3c87507-8ed3-4bdf-8a39-a01bebede295", "Unlock" }, + { "a3c87508-8ed3-4bdf-8a39-a01bebede295", "Public ECDH Key" }, + { "a3c87509-8ed3-4bdf-8a39-a01bebede295", "EID Identity Key" }, + { "a3c8750a-8ed3-4bdf-8a39-a01bebede295", "ADV Slot Data" }, + { "a3c8750b-8ed3-4bdf-8a39-a01bebede295", + "(Advanced) Factory reset" }, + { "a3c8750c-8ed3-4bdf-8a39-a01bebede295", + "(Advanced) Remain Connectable" }, + { } +}; + const char *uuid16_to_str(uint16_t uuid) { int i; @@ -539,14 +585,10 @@ return "Unknown"; } -const char *uuid128_to_str(const unsigned char *uuid) -{ - return "Unknown"; -} - const char *uuidstr_to_str(const char *uuid) { uint32_t val; + int i; if (!uuid) return NULL; @@ -554,6 +596,11 @@ if (strlen(uuid) != 36) return NULL; + for (i = 0; uuid128_table[i].str; i++) { + if (strcasecmp(uuid128_table[i].uuid, uuid) == 0) + return uuid128_table[i].str; + } + if (strncasecmp(uuid + 8, "-0000-1000-8000-00805f9b34fb", 28)) return "Vendor specific"; diff -Nru bluez-5.37/monitor/uuid.h bluez-5.41/monitor/uuid.h --- bluez-5.37/monitor/uuid.h 2014-02-22 01:42:18.000000000 +0000 +++ bluez-5.41/monitor/uuid.h 2016-07-17 21:13:15.000000000 +0000 @@ -26,6 +26,4 @@ const char *uuid16_to_str(uint16_t uuid); const char *uuid32_to_str(uint32_t uuid); -const char *uuid128_to_str(const unsigned char *uuid); - const char *uuidstr_to_str(const char *uuid); diff -Nru bluez-5.37/obexd/client/opp.c bluez-5.41/obexd/client/opp.c --- bluez-5.37/obexd/client/opp.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/obexd/client/opp.c 2016-07-17 21:13:15.000000000 +0000 @@ -117,7 +117,8 @@ static DBusMessage *opp_exchange_business_cards(DBusConnection *connection, DBusMessage *message, void *user_data) { - return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", NULL); + return g_dbus_create_error(message, ERROR_INTERFACE ".Failed", + "Not Implemented"); } static const GDBusMethodTable opp_methods[] = { diff -Nru bluez-5.37/obexd/client/session.c bluez-5.41/obexd/client/session.c --- bluez-5.37/obexd/client/session.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/obexd/client/session.c 2016-03-18 02:18:03.000000000 +0000 @@ -1179,6 +1179,7 @@ if (!data->remaining || !data->remaining[0]) { error("obc_session_setpath: invalid path %s", path); g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument"); + setpath_data_free(data); return 0; } diff -Nru bluez-5.37/obexd/plugins/ftp.c bluez-5.41/obexd/plugins/ftp.c --- bluez-5.37/obexd/plugins/ftp.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/obexd/plugins/ftp.c 2016-07-17 21:13:15.000000000 +0000 @@ -278,6 +278,8 @@ DBG("Fullname: %s", fullname); err = verify_path(fullname); + if (err == -ENOENT) + goto not_found; if (err < 0) goto done; diff -Nru bluez-5.37/obexd/plugins/phonebook-dummy.c bluez-5.41/obexd/plugins/phonebook-dummy.c --- bluez-5.37/obexd/plugins/phonebook-dummy.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/obexd/plugins/phonebook-dummy.c 2016-07-17 21:13:15.000000000 +0000 @@ -520,7 +520,6 @@ struct dummy_data *dummy; char *filename; int fd; - guint ret; filename = g_build_filename(root_folder, folder, id, NULL); @@ -538,13 +537,13 @@ dummy->apparams = params; dummy->fd = fd; - ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy, + dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, read_entry, dummy, dummy_free); if (err) *err = 0; - return GINT_TO_POINTER(ret); + return dummy; } void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, @@ -553,7 +552,7 @@ struct cache_query *query; char *foldername; DIR *dp; - guint ret; + struct dummy_data *dummy; foldername = g_build_filename(root_folder, name, NULL); dp = opendir(foldername); @@ -572,11 +571,13 @@ query->user_data = user_data; query->dp = dp; - ret = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, query, - query_free); + dummy = g_new0(struct dummy_data, 1); + + dummy->id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, create_cache, + query, query_free); if (err) *err = 0; - return GINT_TO_POINTER(ret); + return dummy; } diff -Nru bluez-5.37/profiles/audio/a2dp.c bluez-5.41/profiles/audio/a2dp.c --- bluez-5.37/profiles/audio/a2dp.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/profiles/audio/a2dp.c 2016-04-04 22:21:51.000000000 +0000 @@ -225,6 +225,29 @@ va_end(args); } +static int error_to_errno(struct avdtp_error *err) +{ + int perr; + + if (!err) + return 0; + + if (avdtp_error_category(err) != AVDTP_ERRNO) + return -EIO; + + 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. + */ + return -EAGAIN; + } +} + static gboolean finalize_config(gpointer data) { struct a2dp_setup *s = data; @@ -239,8 +262,8 @@ if (!cb->config_cb) continue; - cb->config_cb(s->session, s->sep, stream, s->err, - cb->user_data); + cb->config_cb(s->session, s->sep, stream, + error_to_errno(s->err), cb->user_data); setup_cb_free(cb); } @@ -260,7 +283,8 @@ if (!cb->resume_cb) continue; - cb->resume_cb(s->session, s->err, cb->user_data); + cb->resume_cb(s->session, error_to_errno(s->err), + cb->user_data); setup_cb_free(cb); } @@ -280,7 +304,8 @@ if (!cb->suspend_cb) continue; - cb->suspend_cb(s->session, s->err, cb->user_data); + cb->suspend_cb(s->session, error_to_errno(s->err), + cb->user_data); setup_cb_free(cb); } @@ -316,7 +341,8 @@ if (!cb->discover_cb) continue; - cb->discover_cb(s->session, s->seps, s->err, cb->user_data); + cb->discover_cb(s->session, s->seps, error_to_errno(s->err), + cb->user_data); setup_cb_free(cb); } } diff -Nru bluez-5.37/profiles/audio/a2dp-codecs.h bluez-5.41/profiles/audio/a2dp-codecs.h --- bluez-5.37/profiles/audio/a2dp-codecs.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/profiles/audio/a2dp-codecs.h 2016-03-18 02:18:03.000000000 +0000 @@ -234,6 +234,11 @@ uint8_t channel_mode:4; } __attribute__ ((packed)) a2dp_aptx_t; +typedef struct { + a2dp_vendor_codec_t info; + uint8_t unknown[2]; +} __attribute__ ((packed)) a2dp_ldac_t; + #else #error "Unknown byte order" #endif diff -Nru bluez-5.37/profiles/audio/a2dp.h bluez-5.41/profiles/audio/a2dp.h --- bluez-5.37/profiles/audio/a2dp.h 2015-07-30 19:17:18.000000000 +0000 +++ bluez-5.41/profiles/audio/a2dp.h 2016-04-04 22:21:51.000000000 +0000 @@ -53,17 +53,14 @@ }; typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps, - struct avdtp_error *err, - void *user_data); + int err, void *user_data); typedef void (*a2dp_select_cb_t) (struct avdtp *session, struct a2dp_sep *sep, GSList *caps, void *user_data); typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, + struct avdtp_stream *stream, int err, void *user_data); -typedef void (*a2dp_stream_cb_t) (struct avdtp *session, - struct avdtp_error *err, +typedef void (*a2dp_stream_cb_t) (struct avdtp *session, int err, void *user_data); struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type, diff -Nru bluez-5.37/profiles/audio/avctp.c bluez-5.41/profiles/audio/avctp.c --- bluez-5.37/profiles/audio/avctp.c 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/profiles/audio/avctp.c 2016-05-26 16:51:11.000000000 +0000 @@ -293,8 +293,9 @@ static void auth_cb(DBusError *derr, void *user_data); static gboolean process_queue(gpointer user_data); static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, void *user_data); + uint8_t subunit, uint8_t transaction, + uint8_t *operands, size_t operand_count, + void *user_data); static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) { @@ -706,8 +707,8 @@ if (p->err == 0 || req->func == NULL) goto done; - req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0, - req->user_data); + req->func(session, AVC_CTYPE_REJECTED, req->subunit, p->transaction, + NULL, 0, req->user_data); done: g_free(req->operands); @@ -829,9 +830,9 @@ continue; if (req->func && req->func(control->session, avc->code, - avc->subunit_type, - operands, operand_count, - req->user_data)) + avc->subunit_type, p->transaction, + operands, operand_count, + req->user_data)) return; control->processed = g_slist_remove(control->processed, p); @@ -1724,8 +1725,9 @@ } static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, void *user_data) + uint8_t subunit, uint8_t transaction, + uint8_t *operands, size_t operand_count, + void *user_data) { if (code != AVC_CTYPE_ACCEPTED) return FALSE; diff -Nru bluez-5.37/profiles/audio/avctp.h bluez-5.41/profiles/audio/avctp.h --- bluez-5.37/profiles/audio/avctp.h 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/profiles/audio/avctp.h 2016-05-26 16:51:11.000000000 +0000 @@ -132,8 +132,9 @@ uint8_t *subunit, uint8_t *operands, size_t operand_count, void *user_data); typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, void *user_data); + uint8_t subunit, uint8_t transaction, + uint8_t *operands, size_t operand_count, + void *user_data); typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session, uint8_t *operands, size_t operand_count, void *user_data); diff -Nru bluez-5.37/profiles/audio/avdtp.c bluez-5.41/profiles/audio/avdtp.c --- bluez-5.37/profiles/audio/avdtp.c 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/profiles/audio/avdtp.c 2016-07-17 21:13:15.000000000 +0000 @@ -1105,6 +1105,8 @@ { char address[18]; + session = avdtp_ref(session); + ba2str(device_get_address(session->device), address); DBG("Disconnected from %s", address); @@ -1115,10 +1117,7 @@ avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); - if (session->ref > 0) - return; - - avdtp_free(session); + avdtp_unref(session); } static gboolean disconnect_timeout(gpointer user_data) @@ -1171,7 +1170,18 @@ if (session->ref > 0) return; - set_disconnect_timer(session); + switch (session->state) { + case AVDTP_SESSION_STATE_CONNECTED: + set_disconnect_timer(session); + break; + case AVDTP_SESSION_STATE_CONNECTING: + connection_lost(session, ECONNABORTED); + break; + case AVDTP_SESSION_STATE_DISCONNECTED: + default: + avdtp_free(session); + break; + } } struct avdtp *avdtp_ref(struct avdtp *session) @@ -1387,6 +1397,7 @@ avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); + stream_free(stream); return; } @@ -3476,7 +3487,7 @@ if (!stream && session->discover) { /* Don't call cb since it being aborted */ session->discover->cb = NULL; - finalize_discovery(session, -ECANCELED); + finalize_discovery(session, ECANCELED); return -EALREADY; } diff -Nru bluez-5.37/profiles/audio/avrcp.c bluez-5.41/profiles/audio/avrcp.c --- bluez-5.37/profiles/audio/avrcp.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/profiles/audio/avrcp.c 2016-07-17 21:13:15.000000000 +0000 @@ -2076,8 +2076,8 @@ } } -static gboolean avrcp_get_play_status_rsp(struct avctp *conn, - uint8_t code, uint8_t subunit, +static gboolean avrcp_get_play_status_rsp(struct avctp *conn, uint8_t code, + uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { @@ -2140,8 +2140,8 @@ } } -static gboolean avrcp_player_value_rsp(struct avctp *conn, - uint8_t code, uint8_t subunit, +static gboolean avrcp_player_value_rsp(struct avctp *conn, uint8_t code, + uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { @@ -2210,8 +2210,8 @@ static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, - uint8_t *operands, size_t operand_count, - void *user_data) + uint8_t transaction, uint8_t *operands, + size_t operand_count, void *user_data) { uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; struct avrcp *session = user_data; @@ -2297,6 +2297,7 @@ static gboolean avrcp_get_element_attributes_rsp(struct avctp *conn, uint8_t code, uint8_t subunit, + uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) @@ -2655,8 +2656,10 @@ uint8_t len; len = pdu->params[i++]; + if (!len) + continue; - if (i + len > operand_count || len == 0) { + if (i + len > operand_count) { error("Invalid folder length"); break; } @@ -3110,7 +3113,7 @@ struct avrcp *session = user_data; struct avrcp_player *player = session->controller->player; struct media_player *mp = player->user_data; - uint32_t num_of_items; + uint32_t num_of_items = 0; if (pdu == NULL) return -ETIMEDOUT; @@ -3197,7 +3200,8 @@ session->controller->player = player; service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); - control_set_player(service, media_player_get_path(player->user_data)); + control_set_player(service, player ? + media_player_get_path(player->user_data) : NULL); } static struct avrcp_player *create_ct_player(struct avrcp *session, @@ -3327,6 +3331,10 @@ struct avrcp_player *player = data; GSList *l; + /* Don't remove reserved player */ + if (!player->id) + return; + for (l = player->sessions; l; l = l->next) { struct avrcp *session = l->data; struct avrcp_data *controller = session->controller; @@ -3390,6 +3398,10 @@ g_slist_free_full(removed, player_remove); + /* There should always be an active player */ + if (!session->controller->player) + create_ct_player(session, 0); + return FALSE; } @@ -3527,8 +3539,8 @@ player->uid_counter = get_be16(&pdu->params[1]); } -static gboolean avrcp_handle_event(struct avctp *conn, - uint8_t code, uint8_t subunit, +static gboolean avrcp_handle_event(struct avctp *conn, uint8_t code, + uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { @@ -3536,16 +3548,30 @@ struct avrcp_header *pdu = (void *) operands; uint8_t event; - if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) || - pdu == NULL) + if (!pdu) + return FALSE; + + if ((code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)) { + if (pdu->params[0] == AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED && + code == AVC_CTYPE_REJECTED) { + int i; + + /* Lookup event by transaction */ + for (i = 0; i <= AVRCP_EVENT_LAST; i++) { + if (session->transaction_events[i] == + transaction) { + event = i; + goto changed; + } + } + } return FALSE; + } event = pdu->params[0]; if (code == AVC_CTYPE_CHANGED) { - session->registered_events ^= (1 << event); - avrcp_register_notification(session, event); - return FALSE; + goto changed; } switch (event) { @@ -3576,8 +3602,15 @@ } session->registered_events |= (1 << event); + session->transaction_events[event] = transaction; return TRUE; + +changed: + session->registered_events ^= (1 << event); + session->transaction_events[event] = 0; + avrcp_register_notification(session, event); + return FALSE; } static void avrcp_register_notification(struct avrcp *session, uint8_t event) @@ -3609,8 +3642,8 @@ avrcp_handle_event, session); } -static gboolean avrcp_get_capabilities_resp(struct avctp *conn, - uint8_t code, uint8_t subunit, +static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, + uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { @@ -3645,8 +3678,9 @@ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: case AVRCP_EVENT_UIDS_CHANGED: case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: - /* These events above are controller specific */ - if (!session->controller) + /* These events above requires a player */ + if (!session->controller || + !session->controller->player) break; case AVRCP_EVENT_VOLUME_CHANGED: avrcp_register_notification(session, event); @@ -3654,7 +3688,7 @@ } } - if (!session->controller) + if (!session->controller || !session->controller->player) return FALSE; if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED))) @@ -3843,12 +3877,11 @@ btd_service_connecting_complete(service, 0); /* Only create player if category 1 is supported */ - if (!(controller->features & AVRCP_FEATURE_CATEGORY_1)) - return; - - player = create_ct_player(session, 0); - if (player == NULL) - return; + if (controller->features & AVRCP_FEATURE_CATEGORY_1) { + player = create_ct_player(session, 0); + if (player == NULL) + return; + } if (controller->version < 0x0103) return; @@ -4124,8 +4157,8 @@ player_destroy(player); } -static gboolean avrcp_handle_set_volume(struct avctp *conn, - uint8_t code, uint8_t subunit, +static gboolean avrcp_handle_set_volume(struct avctp *conn, uint8_t code, + uint8_t subunit, uint8_t transaction, uint8_t *operands, size_t operand_count, void *user_data) { diff -Nru bluez-5.37/profiles/audio/media.c bluez-5.41/profiles/audio/media.c --- bluez-5.37/profiles/audio/media.c 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/profiles/audio/media.c 2016-05-26 16:51:11.000000000 +0000 @@ -1018,6 +1018,9 @@ { struct media_player *mp = user_data; + if (!mp->name) + return "Player"; + return mp->name; } diff -Nru bluez-5.37/profiles/audio/sink.c bluez-5.41/profiles/audio/sink.c --- bluez-5.37/profiles/audio/sink.c 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/profiles/audio/sink.c 2016-04-04 22:21:51.000000000 +0000 @@ -180,8 +180,8 @@ } static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) + struct avdtp_stream *stream, int err, + void *user_data) { struct sink *sink = user_data; @@ -192,11 +192,7 @@ avdtp_unref(sink->session); sink->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) - btd_service_connecting_complete(sink->service, -EAGAIN); - else - btd_service_connecting_complete(sink->service, -EIO); + btd_service_connecting_complete(sink->service, err); } static void select_complete(struct avdtp *session, struct a2dp_sep *sep, @@ -221,25 +217,17 @@ sink->session = NULL; } -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, - void *user_data) +static void discovery_complete(struct avdtp *session, GSList *seps, int err, + void *user_data) { struct sink *sink = user_data; - int id, perr; + int id; sink->connect_id = 0; if (err) { avdtp_unref(sink->session); sink->session = NULL; - - perr = -avdtp_error_posix_errno(err); - if (perr != -EHOSTDOWN) { - if (avdtp_error_category(err) == AVDTP_ERRNO) - perr = -EAGAIN; - else - perr = -EIO; - } goto failed; } @@ -248,7 +236,7 @@ id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL, select_complete, sink); if (id == 0) { - perr = -EIO; + err = -EIO; goto failed; } @@ -256,7 +244,7 @@ return; failed: - btd_service_connecting_complete(sink->service, perr); + btd_service_connecting_complete(sink->service, err); avdtp_unref(sink->session); sink->session = NULL; } @@ -408,13 +396,13 @@ /* cancel pending connect */ if (sink->connect_id > 0) { + avdtp_unref(sink->session); + sink->session = NULL; + a2dp_cancel(sink->connect_id); sink->connect_id = 0; btd_service_disconnecting_complete(sink->service, 0); - avdtp_unref(sink->session); - sink->session = NULL; - return 0; } diff -Nru bluez-5.37/profiles/audio/source.c bluez-5.41/profiles/audio/source.c --- bluez-5.37/profiles/audio/source.c 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/profiles/audio/source.c 2016-04-04 22:21:51.000000000 +0000 @@ -177,8 +177,8 @@ } static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) + struct avdtp_stream *stream, int err, + void *user_data) { struct source *source = user_data; @@ -189,11 +189,7 @@ avdtp_unref(source->session); source->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) - btd_service_connecting_complete(source->service, -EAGAIN); - else - btd_service_connecting_complete(source->service, -EIO); + btd_service_connecting_complete(source->service, err); } static void select_complete(struct avdtp *session, struct a2dp_sep *sep, @@ -221,34 +217,26 @@ source->session = NULL; } -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, - void *user_data) +static void discovery_complete(struct avdtp *session, GSList *seps, int err, + void *user_data) { struct source *source = user_data; - int id, perr; + int id; source->connect_id = 0; if (err) { avdtp_unref(source->session); source->session = NULL; - - perr = -avdtp_error_posix_errno(err); - if (perr != -EHOSTDOWN) { - if (avdtp_error_category(err) == AVDTP_ERRNO) - perr = -EAGAIN; - else - perr = -EIO; - } goto failed; } DBG("Discovery complete"); - id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL, - select_complete, source); + id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, + NULL, select_complete, source); if (id == 0) { - perr = -EIO; + err = -EIO; goto failed; } @@ -256,7 +244,7 @@ return; failed: - btd_service_connecting_complete(source->service, perr); + btd_service_connecting_complete(source->service, err); avdtp_unref(source->session); source->session = NULL; } @@ -400,13 +388,13 @@ /* cancel pending connect */ if (source->connect_id > 0) { + avdtp_unref(source->session); + source->session = NULL; + a2dp_cancel(source->connect_id); source->connect_id = 0; btd_service_disconnecting_complete(source->service, 0); - avdtp_unref(source->session); - source->session = NULL; - return 0; } diff -Nru bluez-5.37/profiles/audio/transport.c bluez-5.41/profiles/audio/transport.c --- bluez-5.37/profiles/audio/transport.c 2015-09-28 12:16:53.000000000 +0000 +++ bluez-5.41/profiles/audio/transport.c 2016-04-04 22:21:51.000000000 +0000 @@ -283,8 +283,8 @@ return TRUE; } -static void a2dp_resume_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) +static void a2dp_resume_complete(struct avdtp *session, int err, + void *user_data) { struct media_owner *owner = user_data; struct media_request *req = owner->pending; @@ -362,8 +362,8 @@ return id; } -static void a2dp_suspend_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) +static void a2dp_suspend_complete(struct avdtp *session, int err, + void *user_data) { struct media_owner *owner = user_data; struct media_transport *transport = owner->transport; diff -Nru bluez-5.37/profiles/deviceinfo/deviceinfo.c bluez-5.41/profiles/deviceinfo/deviceinfo.c --- bluez-5.37/profiles/deviceinfo/deviceinfo.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/profiles/deviceinfo/deviceinfo.c 2016-05-26 16:51:11.000000000 +0000 @@ -88,7 +88,7 @@ bt_string_to_uuid(&pnpid_uuid, PNPID_UUID); if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, - &uuid)) { + NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } diff -Nru bluez-5.37/profiles/gap/gas.c bluez-5.41/profiles/gap/gas.c --- bluez-5.37/profiles/gap/gas.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/profiles/gap/gas.c 2016-05-26 16:51:11.000000000 +0000 @@ -181,7 +181,7 @@ bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, - &uuid)) { + NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } diff -Nru bluez-5.37/profiles/input/hog.c bluez-5.41/profiles/input/hog.c --- bluez-5.37/profiles/input/hog.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/profiles/input/hog.c 2016-04-04 22:21:51.000000000 +0000 @@ -107,8 +107,13 @@ product, version); dev = new0(struct hog_device, 1); - dev->device = btd_device_ref(device); dev->hog = bt_hog_new_default(name, vendor, product, version, prim); + if (!dev->hog) { + free(dev); + return NULL; + } + + dev->device = btd_device_ref(device); /* * TODO: Remove attio callback and use .accept once using @@ -189,6 +194,9 @@ continue; dev = hog_device_new(device, prim); + if (!dev) + break; + btd_service_set_user_data(service, dev); return 0; } diff -Nru bluez-5.37/profiles/scanparam/scan.c bluez-5.41/profiles/scanparam/scan.c --- bluez-5.37/profiles/scanparam/scan.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/profiles/scanparam/scan.c 2016-05-26 16:51:11.000000000 +0000 @@ -140,7 +140,7 @@ bt_uuid_t uuid, scan_interval_wind_uuid, scan_refresh_uuid; if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, - &uuid)) { + NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } diff -Nru bluez-5.37/src/adapter.c bluez-5.41/src/adapter.c --- bluez-5.37/src/adapter.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/adapter.c 2016-07-17 21:13:15.000000000 +0000 @@ -2187,9 +2187,7 @@ *transport = SCAN_TYPE_BREDR; else if (!strcmp(transport_str, "le")) *transport = SCAN_TYPE_LE; - else if (!strcmp(transport_str, "auto")) - *transport = SCAN_TYPE_DUAL; - else + else if (strcmp(transport_str, "auto")) return false; return true; @@ -2220,8 +2218,9 @@ * successful, sets *filter to proper value. * Returns false on any error, and true on success. */ -static bool parse_discovery_filter_dict(struct discovery_filter **filter, - DBusMessage *msg) +static bool parse_discovery_filter_dict(struct btd_adapter *adapter, + struct discovery_filter **filter, + DBusMessage *msg) { DBusMessageIter iter, subiter, dictiter, variantiter; bool is_empty = true; @@ -2233,7 +2232,7 @@ (*filter)->uuids = NULL; (*filter)->pathloss = DISTANCE_VAL_INVALID; (*filter)->rssi = DISTANCE_VAL_INVALID; - (*filter)->type = SCAN_TYPE_DUAL; + (*filter)->type = get_scan_type(adapter); dbus_message_iter_init(msg, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -2304,8 +2303,11 @@ if (!(adapter->current_settings & MGMT_SETTING_POWERED)) return btd_error_not_ready(msg); + if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 8)) + return btd_error_not_supported(msg); + /* parse parameters */ - if (!parse_discovery_filter_dict(&discovery_filter, msg)) + if (!parse_discovery_filter_dict(adapter, &discovery_filter, msg)) return btd_error_invalid_args(msg); is_discovering = get_discovery_client(adapter, sender, &client); @@ -3088,12 +3090,12 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer, uint8_t bdaddr_type) { - struct irk_info *irk; + struct irk_info *irk = NULL; char *str; str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL); if (!str || strlen(str) < 32) - return NULL; + goto failed; irk = g_new0(struct irk_info, 1); @@ -3105,6 +3107,7 @@ else str2buf(&str[0], irk->val, sizeof(irk->val)); +failed: g_free(str); return irk; @@ -5447,7 +5450,7 @@ memset(&eir_data, 0, sizeof(eir_data)); eir_parse(&eir_data, data, data_len); - if (bdaddr_type == BDADDR_BREDR) + if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery) discoverable = true; else discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC); diff -Nru bluez-5.37/src/adapter.h bluez-5.41/src/adapter.h --- bluez-5.37/src/adapter.h 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/src/adapter.h 2016-03-18 02:18:03.000000000 +0000 @@ -225,3 +225,4 @@ void *data); bool btd_le_connect_before_pairing(void); + diff -Nru bluez-5.37/src/advertising.c bluez-5.41/src/advertising.c --- bluez-5.37/src/advertising.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/advertising.c 2016-03-18 02:18:03.000000000 +0000 @@ -500,7 +500,7 @@ return btd_error_failed(ad->reg, "Failed"); } - cp->flags = flags; + cp->flags = htobl(flags); cp->instance = ad->instance; cp->adv_data_len = adv_data_len; memcpy(cp->data, adv_data, adv_data_len); diff -Nru bluez-5.37/src/bluetooth.service.in bluez-5.41/src/bluetooth.service.in --- bluez-5.37/src/bluetooth.service.in 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/src/bluetooth.service.in 2016-05-26 16:51:11.000000000 +0000 @@ -12,6 +12,8 @@ #Restart=on-failure CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE LimitNPROC=1 +ProtectHome=true +ProtectSystem=full [Install] WantedBy=bluetooth.target diff -Nru bluez-5.37/src/device.c bluez-5.41/src/device.c --- bluez-5.37/src/device.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/device.c 2016-07-17 21:13:15.000000000 +0000 @@ -238,7 +238,6 @@ * attribute cache support can be built. */ struct gatt_db *db; /* GATT db cache */ - bool gatt_cache_used; /* true if discovery skipped */ struct bt_gatt_client *client; /* GATT client instance */ struct bt_gatt_server *server; /* GATT server instance */ @@ -949,38 +948,14 @@ return TRUE; } -static void append_service_path(const char *path, void *user_data) -{ - DBusMessageIter *array = user_data; - - dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, &path); -} - -static gboolean dev_property_get_gatt_services( - const GDBusPropertyTable *property, +static gboolean +dev_property_get_svc_resolved(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { - struct btd_device *dev = data; - DBusMessageIter array; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array); - - btd_gatt_client_foreach_service(dev->client_dbus, append_service_path, - &array); - - dbus_message_iter_close_container(iter, &array); - - return TRUE; -} - -static gboolean dev_property_exists_gatt_services( - const GDBusPropertyTable *property, - void *data) -{ - struct btd_device *dev = data; + struct btd_device *device = data; + gboolean val = device->svc_refreshed; - if (!dev->client || !bt_gatt_client_is_ready(dev->client)) - return FALSE; + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); return TRUE; } @@ -1258,6 +1233,9 @@ if (device->blocked) return 0; + if (device->disconn_timer > 0) + g_source_remove(device->disconn_timer); + disconnect_all(device); while (device->services != NULL) { @@ -1982,6 +1960,7 @@ struct gatt_saver { struct btd_device *device; + uint16_t ext_props; GKeyFile *key_file; }; @@ -1991,6 +1970,7 @@ GKeyFile *key_file = saver->key_file; char handle[6], value[100], uuid_str[MAX_LEN_UUID_STR]; const bt_uuid_t *uuid; + bt_uuid_t ext_uuid; uint16_t handle_num; handle_num = gatt_db_attribute_get_handle(attr); @@ -1998,7 +1978,13 @@ uuid = gatt_db_attribute_get_type(attr); bt_uuid_to_string(uuid, uuid_str, sizeof(uuid_str)); - sprintf(value, "%s", uuid_str); + + bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID); + if (!bt_uuid_cmp(uuid, &ext_uuid) && saver->ext_props) + sprintf(value, "%04hx:%s", saver->ext_props, uuid_str); + else + sprintf(value, "%s", uuid_str); + g_key_file_set_string(key_file, "Attributes", handle, value); } @@ -2012,7 +1998,8 @@ bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, &handle_num, &value_handle, - &properties, &uuid)) { + &properties, &saver->ext_props, + &uuid)) { warn("Error storing characteristic - can't get data"); return; } @@ -2114,6 +2101,9 @@ key_file = g_key_file_new(); g_key_file_load_from_file(key_file, filename, 0, NULL); + /* Remove current attributes since it might have changed */ + g_key_file_remove_group(key_file, "Attributes", NULL); + saver.key_file = key_file; saver.device = device; @@ -2173,6 +2163,17 @@ browse_request_free(req); } +static void device_set_svc_refreshed(struct btd_device *device, bool value) +{ + if (device->svc_refreshed == value) + return; + + device->svc_refreshed = value; + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "ServicesResolved"); +} + static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type, int err) { @@ -2188,7 +2189,7 @@ * device. */ if (state->connected) - dev->svc_refreshed = true; + device_set_svc_refreshed(dev, true); g_slist_free_full(dev->eir_uuids, g_free); dev->eir_uuids = NULL; @@ -2530,8 +2531,7 @@ { "TxPower", "n", dev_property_get_tx_power, NULL, dev_property_exists_tx_power, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, - { "GattServices", "ao", dev_property_get_gatt_services, NULL, - dev_property_exists_gatt_services, + { "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } @@ -2583,9 +2583,10 @@ return; state->connected = false; - device->svc_refreshed = false; device->general_connect = FALSE; + device_set_svc_refreshed(device, false); + if (device->disconn_timer > 0) { g_source_remove(device->disconn_timer); device->disconn_timer = 0; @@ -2654,6 +2655,9 @@ char *str = NULL; int len; + if (device_address_is_private(device)) + return NULL; + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer); key_file = g_key_file_new(); @@ -2708,6 +2712,61 @@ return NULL; } +static void load_services(struct btd_device *device, char **uuids) +{ + char **uuid; + + for (uuid = uuids; *uuid; uuid++) { + if (g_slist_find_custom(device->uuids, *uuid, bt_uuid_strcmp)) + continue; + + device->uuids = g_slist_insert_sorted(device->uuids, + g_strdup(*uuid), + bt_uuid_strcmp); + } + + g_strfreev(uuids); +} + +static void convert_info(struct btd_device *device, GKeyFile *key_file) +{ + char filename[PATH_MAX]; + char adapter_addr[18]; + char device_addr[18]; + char **uuids; + char *str; + gsize length = 0; + + /* Load device profile list from legacy properties */ + uuids = g_key_file_get_string_list(key_file, "General", "SDPServices", + NULL, NULL); + if (uuids) + load_services(device, uuids); + + uuids = g_key_file_get_string_list(key_file, "General", "GATTServices", + NULL, NULL); + if (uuids) + load_services(device, uuids); + + if (!device->uuids) + return; + + /* Remove old entries so they are not loaded again */ + g_key_file_remove_key(key_file, "General", "SDPServices", NULL); + g_key_file_remove_key(key_file, "General", "GATTServices", NULL); + + ba2str(btd_adapter_get_address(device->adapter), adapter_addr); + ba2str(&device->bdaddr, device_addr); + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, + device_addr); + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(filename, str, length, NULL); + g_free(str); + + store_device_info(device); +} + static void load_info(struct btd_device *device, const char *local, const char *peer, GKeyFile *key_file) { @@ -2804,21 +2863,7 @@ uuids = g_key_file_get_string_list(key_file, "General", "Services", NULL, NULL); if (uuids) { - char **uuid; - - for (uuid = uuids; *uuid; uuid++) { - GSList *match; - - match = g_slist_find_custom(device->uuids, *uuid, - bt_uuid_strcmp); - if (match) - continue; - - device->uuids = g_slist_insert_sorted(device->uuids, - g_strdup(*uuid), - bt_uuid_strcmp); - } - g_strfreev(uuids); + load_services(device, uuids); /* Discovered services restored from storage */ device->bredr_state.svc_resolved = true; @@ -2957,24 +3002,43 @@ *new_services = g_slist_append(*new_services, prim); } +static void load_desc_value(struct gatt_db_attribute *attrib, + int err, void *user_data) +{ + if (err) + warn("loading descriptor value to db failed"); +} + static int load_desc(char *handle, char *value, struct gatt_db_attribute *service) { char uuid_str[MAX_LEN_UUID_STR]; struct gatt_db_attribute *att; uint16_t handle_int; - bt_uuid_t uuid; + uint16_t val; + bt_uuid_t uuid, ext_uuid; if (sscanf(handle, "%04hx", &handle_int) != 1) return -EIO; - if (sscanf(value, "%s", uuid_str) != 1) - return -EIO; + /* Check if there is any value stored, otherwise it is just the UUID */ + if (sscanf(value, "%04hx:%s", &val, uuid_str) != 2) { + if (sscanf(value, "%s", uuid_str) != 1) + return -EIO; + val = 0; + } + + DBG("loading descriptor handle: 0x%04x, value: 0x%04x, uuid: %s", + handle_int, val, uuid_str); bt_string_to_uuid(&uuid, uuid_str); + bt_uuid16_create(&ext_uuid, GATT_CHARAC_EXT_PROPER_UUID); - DBG("loading descriptor handle: 0x%04x, uuid: %s", handle_int, - uuid_str); + /* If it is CEP then it must contain the value */ + if (!bt_uuid_cmp(&uuid, &ext_uuid) && !val) { + warn("cannot load CEP descriptor without value"); + return -EIO; + } att = gatt_db_service_insert_descriptor(service, handle_int, &uuid, 0, NULL, NULL, NULL); @@ -2983,6 +3047,13 @@ return -EIO; } + if (val) { + if (!gatt_db_attribute_write(att, 0, (uint8_t *)&val, + sizeof(val), 0, NULL, + load_desc_value, NULL)) + return -EIO; + } + return 0; } @@ -3012,7 +3083,7 @@ &uuid, 0, properties, NULL, NULL, NULL); if (!att || gatt_db_attribute_get_handle(att) != value_handle) { - warn("saving characteristic to db failed"); + warn("loading characteristic to db failed"); return -EIO; } @@ -3039,13 +3110,13 @@ att = gatt_db_get_attribute(db, start); if (!att) { - warn("saving included service to db failed - no such service"); + warn("loading included service to db failed - no such service"); return -EIO; } att = gatt_db_service_add_included(service, att); if (!att) { - warn("saving included service to db failed"); + warn("loading included service to db failed"); return -EIO; } @@ -3082,7 +3153,7 @@ att = gatt_db_insert_service(db, start, &uuid, primary, end - start + 1); if (!att) { - DBG("ERROR saving service to db!"); + error("Unable load service into db!"); return -EIO; } @@ -3165,8 +3236,10 @@ } g_free(value); - if (ret) + if (ret) { + gatt_db_clear(db); return ret; + } } if (current_service) @@ -3334,9 +3407,6 @@ store_gatt_db(device); - g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, - "GattServices"); - return FALSE; } @@ -3537,6 +3607,8 @@ src = btd_adapter_get_address(adapter); ba2str(src, srcaddr); + convert_info(device, key_file); + load_info(device, srcaddr, address, key_file); load_att_info(device, srcaddr, address); @@ -3868,8 +3940,10 @@ g_slist_free(device->pending); device->pending = NULL; - if (btd_device_is_connected(device)) + if (btd_device_is_connected(device)) { + g_source_remove(device->disconn_timer); disconnect_all(device); + } if (device->store_id > 0) { g_source_remove(device->store_id); @@ -4524,8 +4598,6 @@ device_register_primaries(device, services, -1); device_add_gatt_services(device); - - device_svc_resolved(device, device->bdaddr_type, 0); } static void gatt_client_init(struct btd_device *device); @@ -4546,18 +4618,9 @@ btd_gatt_client_ready(device->client_dbus); - /* - * Update the GattServices property. Do this asynchronously since this - * should happen after the "Characteristics" and "Descriptors" - * properties of all services have been asynchronously updated by - * btd_gatt_client. - * - * Service discovery will be skipped and exported objects won't change - * if the attribute cache was populated when bt_gatt_client gets - * initialized, so no need to to send this signal if that's the case. - */ - if (!device->gatt_cache_used) - g_idle_add(gatt_services_changed, device); + device_svc_resolved(device, device->bdaddr_type, 0); + + store_gatt_db(device); } static void gatt_client_service_changed(uint16_t start_handle, @@ -4610,8 +4673,6 @@ return; } - device->gatt_cache_used = !gatt_db_isempty(device->db); - btd_gatt_client_connected(device->client_dbus); } @@ -4787,10 +4848,10 @@ bonding_request_free(device->bonding); } - if (device->connect) { - if (!device->le_state.svc_resolved && !err) - device_browse_gatt(device, NULL); + if (!err) + device_browse_gatt(device, NULL); + if (device->connect) { if (err < 0) reply = btd_error_failed(device->connect, strerror(-err)); @@ -4936,12 +4997,12 @@ if (!req) return -EBUSY; - if (device->attrib) { + if (device->client) { /* * If discovery has not yet completed, then wait for gatt-client * to become ready. */ - if (!device->le_state.svc_resolved) + if (!bt_gatt_client_is_ready(device->client)) return 0; /* @@ -5080,6 +5141,9 @@ if (device->temporary == temporary) return; + if (device_address_is_private(device)) + return; + DBG("temporary %d", temporary); device->temporary = temporary; diff -Nru bluez-5.37/src/gatt-client.c bluez-5.41/src/gatt-client.c --- bluez-5.37/src/gatt-client.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/gatt-client.c 2016-07-17 21:13:15.000000000 +0000 @@ -23,6 +23,7 @@ #include #include +#include #include @@ -71,9 +72,17 @@ bt_uuid_t uuid; char *path; struct queue *chrcs; - bool chrcs_ready; - struct queue *pending_ext_props; - guint idle_id; +}; + +typedef bool (*async_dbus_op_complete_t)(void *data); + +struct async_dbus_op { + int ref_count; + unsigned int id; + struct queue *msgs; + void *data; + uint16_t offset; + async_dbus_op_complete_t complete; }; struct characteristic { @@ -87,8 +96,8 @@ bt_uuid_t uuid; char *path; - unsigned int read_id; - unsigned int write_id; + struct async_dbus_op *read_op; + struct async_dbus_op *write_op; struct queue *descs; @@ -103,8 +112,8 @@ bt_uuid_t uuid; char *path; - unsigned int read_id; - unsigned int write_id; + struct async_dbus_op *read_op; + struct async_dbus_op *write_op; }; static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16) @@ -194,51 +203,24 @@ return ret; } -static bool parse_value_arg(DBusMessage *msg, uint8_t **value, - size_t *value_len) +static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) { - DBusMessageIter iter, array; - uint8_t *val; - int len; - - if (!dbus_message_iter_init(msg, &iter)) - return false; - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) - return false; - - dbus_message_iter_recurse(&iter, &array); - dbus_message_iter_get_fixed_array(&array, &val, &len); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) - return false; + DBusMessageIter array; - if (len < 0) - return false; + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; - *value = val; - *value_len = len; + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, value, len); - return true; + return 0; } -typedef bool (*async_dbus_op_complete_t)(void *data); - -struct async_dbus_op { - int ref_count; - DBusMessage *msg; - void *data; - uint16_t offset; - async_dbus_op_complete_t complete; -}; - static void async_dbus_op_free(void *data) { struct async_dbus_op *op = data; - if (op->msg) - dbus_message_unref(op->msg); + queue_destroy(op->msgs, (void *)dbus_message_unref); free(op); } @@ -314,27 +296,44 @@ GATT_DESCRIPTOR_IFACE, "Value"); } -static void read_op_cb(struct gatt_db_attribute *attrib, int err, - const uint8_t *value, size_t length, - void *user_data) +static void async_dbus_op_reply(struct async_dbus_op *op, int err, + const uint8_t *value, size_t length) { - struct async_dbus_op *op = user_data; + const struct queue_entry *entry; DBusMessage *reply; - if (err) { - error("Failed to read attribute"); - return; - } + op->id = 0; - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - if (!reply) { - error("Failed to allocate D-Bus message reply"); - return; + for (entry = queue_get_entries(op->msgs); entry; entry = entry->next) { + DBusMessage *msg = entry->data; + + if (err) { + reply = err > 0 ? create_gatt_dbus_error(msg, err) : + btd_error_failed(msg, strerror(err)); + goto send_reply; + } + + reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + if (!reply) { + error("Failed to allocate D-Bus message reply"); + return; + } + + if (value) + message_append_byte_array(reply, value, length); + +send_reply: + g_dbus_send_message(btd_get_dbus_connection(), reply); } +} - message_append_byte_array(reply, value, length); +static void read_op_cb(struct gatt_db_attribute *attrib, int err, + const uint8_t *value, size_t length, + void *user_data) +{ + struct async_dbus_op *op = user_data; - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, err, value, length); } static void desc_read_cb(bool success, uint8_t att_ecode, @@ -343,8 +342,6 @@ { struct async_dbus_op *op = user_data; struct descriptor *desc = op->data; - struct service *service = desc->chrc->service; - DBusMessage *reply; if (!success) goto fail; @@ -355,42 +352,91 @@ if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL, write_descriptor_cb, desc)) { error("Failed to store attribute"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } - /* - * If the value length is exactly MTU-1, then we may not have read the - * entire value. Perform a long read to obtain the rest, otherwise, - * we're done. - */ - if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { - op->offset += length; - desc->read_id = bt_gatt_client_read_long_value( - service->client->gatt, - desc->handle, - op->offset, - desc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); - if (desc->read_id) - return; - } - /* Read the stored data from db */ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } - desc->read_id = 0; + desc->read_op = NULL; return; fail: - reply = create_gatt_dbus_error(op->msg, att_ecode); - desc->read_id = 0; - g_dbus_send_message(btd_get_dbus_connection(), reply); - return; + async_dbus_op_reply(op, att_ecode, NULL, 0); + desc->read_op = NULL; +} + +static int parse_options(DBusMessageIter *iter, uint16_t *offset) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + if (strcasecmp(key, "offset") == 0) { + if (var != DBUS_TYPE_UINT16) + return -EINVAL; + dbus_message_iter_get_basic(&value, offset); + } + + dbus_message_iter_next(&dict); + } + + return 0; +} + +static struct async_dbus_op *async_dbus_op_new(DBusMessage *msg, void *data) +{ + struct async_dbus_op *op; + + op = new0(struct async_dbus_op, 1); + op->msgs = queue_new(); + queue_push_tail(op->msgs, dbus_message_ref(msg)); + op->data = data; + + return op; +} + +static struct async_dbus_op *read_value(struct bt_gatt_client *gatt, + DBusMessage *msg, uint16_t handle, + uint16_t offset, + bt_gatt_client_read_callback_t callback, + void *data) +{ + struct async_dbus_op *op; + + op = async_dbus_op_new(msg, data); + op->offset = offset; + + op->id = bt_gatt_client_read_long_value(gatt, handle, offset, callback, + async_dbus_op_ref(op), + async_dbus_op_unref); + if (op->id) + return op; + + async_dbus_op_free(op); + + return NULL; } static DBusMessage *descriptor_read_value(DBusConnection *conn, @@ -398,120 +444,113 @@ { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; - struct async_dbus_op *op; + DBusMessageIter iter; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (desc->read_id) - return btd_error_in_progress(msg); + dbus_message_iter_init(msg, &iter); - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = desc; + if (parse_options(&iter, &offset)) + return btd_error_invalid_args(msg); - desc->read_id = bt_gatt_client_read_value(gatt, desc->handle, - desc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); - if (desc->read_id) + if (desc->read_op) { + if (desc->read_op->offset != offset) + return btd_error_in_progress(msg); + queue_push_tail(desc->read_op->msgs, dbus_message_ref(msg)); return NULL; + } - async_dbus_op_free(op); + desc->read_op = read_value(gatt, msg, desc->handle, offset, + desc_read_cb, desc); + if (!desc->read_op) + return btd_error_failed(msg, "Failed to send read request"); - return btd_error_failed(msg, "Failed to send read request"); + return NULL; } static void write_result_cb(bool success, bool reliable_error, uint8_t att_ecode, void *user_data) { struct async_dbus_op *op = user_data; - DBusMessage *reply; + int err = 0; if (op->complete && !op->complete(op->data)) { - reply = btd_error_failed(op->msg, "Operation failed"); + err = -EFAULT; goto done; } if (!success) { if (reliable_error) - reply = btd_error_failed(op->msg, - "Reliable write failed"); + err = -EFAULT; else - reply = create_gatt_dbus_error(op->msg, att_ecode); - - goto done; - } - - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - if (!reply) { - error("Failed to allocate D-Bus message reply"); - return; + err = att_ecode; } done: - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, err, NULL, 0); } - static void write_cb(bool success, uint8_t att_ecode, void *user_data) { write_result_cb(success, false, att_ecode, user_data); } -static unsigned int start_long_write(DBusMessage *msg, uint16_t handle, +static struct async_dbus_op *start_long_write(DBusMessage *msg, uint16_t handle, struct bt_gatt_client *gatt, bool reliable, const uint8_t *value, - size_t value_len, void *data, + size_t value_len, uint16_t offset, + void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; - unsigned int id; - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = data; + op = async_dbus_op_new(msg, data); op->complete = complete; + op->offset = offset; - id = bt_gatt_client_write_long_value(gatt, reliable, handle, - 0, value, value_len, + op->id = bt_gatt_client_write_long_value(gatt, reliable, handle, offset, + value, value_len, write_result_cb, op, async_dbus_op_free); - if (!id) + if (!op->id) { async_dbus_op_free(op); + return NULL; + } - return id; + return op; } -static unsigned int start_write_request(DBusMessage *msg, uint16_t handle, +static struct async_dbus_op *start_write_request(DBusMessage *msg, + uint16_t handle, struct bt_gatt_client *gatt, const uint8_t *value, size_t value_len, void *data, async_dbus_op_complete_t complete) { struct async_dbus_op *op; - unsigned int id; - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = data; + op = async_dbus_op_new(msg, data); op->complete = complete; - id = bt_gatt_client_write_value(gatt, handle, value, value_len, + op->id = bt_gatt_client_write_value(gatt, handle, value, value_len, write_cb, op, async_dbus_op_free); - if (!id) + if (!op->id) { async_dbus_op_free(op); + return NULL; + } - return id; + return op; } static bool desc_write_complete(void *data) { struct descriptor *desc = data; - desc->write_id = 0; + desc->write_op = NULL; /* * The descriptor might have been unregistered during the read. Return @@ -525,16 +564,23 @@ { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; + DBusMessageIter iter; uint8_t *value = NULL; - size_t value_len = 0; + int value_len = 0; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (desc->write_id) + if (desc->write_op) return btd_error_in_progress(msg); - if (!parse_value_arg(msg, &value, &value_len)) + dbus_message_iter_init(msg, &iter); + + if (parse_value_arg(&iter, &value, &value_len)) + return btd_error_invalid_args(msg); + + if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); /* @@ -549,18 +595,18 @@ * Based on the value length and the MTU, either use a write or a long * write. */ - if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3) - desc->write_id = start_write_request(msg, desc->handle, + if (value_len <= bt_gatt_client_get_mtu(gatt) - 3 && !offset) + desc->write_op = start_write_request(msg, desc->handle, gatt, value, value_len, desc, desc_write_complete); else - desc->write_id = start_long_write(msg, desc->handle, - gatt, false, value, - value_len, desc, + desc->write_op = start_long_write(msg, desc->handle, gatt, + false, value, + value_len, offset, desc, desc_write_complete); - if (!desc->write_id) + if (!desc->write_op) return btd_error_failed(msg, "Failed to initiate write"); return NULL; @@ -577,13 +623,15 @@ }; static const GDBusMethodTable descriptor_methods[] = { - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL, - GDBUS_ARGS({ "value", "ay" }), - descriptor_read_value) }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", + GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + descriptor_read_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue", - GDBUS_ARGS({ "value", "ay" }), - NULL, - descriptor_write_value) }, + GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, + descriptor_write_value) }, { } }; @@ -636,11 +684,11 @@ DBG("Removing GATT descriptor: %s", desc->path); - if (desc->read_id) - bt_gatt_client_cancel(gatt, desc->read_id); + if (desc->read_op) + bt_gatt_client_cancel(gatt, desc->read_op->id); - if (desc->write_id) - bt_gatt_client_cancel(gatt, desc->write_id); + if (desc->write_op) + bt_gatt_client_cancel(gatt, desc->write_op->id); desc->chrc = NULL; @@ -709,6 +757,15 @@ return TRUE; } +static gboolean +characteristic_notifying_exists(const GDBusPropertyTable *property, void *data) +{ + struct characteristic *chrc = data; + + return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY || + chrc->props & BT_GATT_CHRC_PROP_INDICATE); +} + struct chrc_prop_data { uint8_t prop; char *str; @@ -777,8 +834,6 @@ { struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; - struct service *service = chrc->service; - DBusMessage *reply; if (!success) goto fail; @@ -789,41 +844,24 @@ if (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL, write_characteristic_cb, chrc)) { error("Failed to store attribute"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } - /* - * If the value length is exactly MTU-1, then we may not have read the - * entire value. Perform a long read to obtain the rest, otherwise, - * we're done. - */ - if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { - op->offset += length; - chrc->read_id = bt_gatt_client_read_long_value( - service->client->gatt, - chrc->value_handle, - op->offset, - chrc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); - if (chrc->read_id) - return; - } - - chrc->read_id = 0; - /* Read the stored data from db */ if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); + att_ecode = BT_ATT_ERROR_UNLIKELY; goto fail; } + chrc->read_op = NULL; + return; fail: - reply = create_gatt_dbus_error(op->msg, att_ecode); - chrc->read_id = 0; - g_dbus_send_message(btd_get_dbus_connection(), reply); + async_dbus_op_reply(op, att_ecode, NULL, 0); + chrc->read_op = NULL; } static DBusMessage *characteristic_read_value(DBusConnection *conn, @@ -831,35 +869,37 @@ { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; - struct async_dbus_op *op; + DBusMessageIter iter; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (chrc->read_id) - return btd_error_in_progress(msg); + dbus_message_iter_init(msg, &iter); - op = new0(struct async_dbus_op, 1); - op->msg = dbus_message_ref(msg); - op->data = chrc; + if (parse_options(&iter, &offset)) + return btd_error_invalid_args(msg); - chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle, - chrc_read_cb, - async_dbus_op_ref(op), - async_dbus_op_unref); - if (chrc->read_id) + if (chrc->read_op) { + if (chrc->read_op->offset != offset) + return btd_error_in_progress(msg); + queue_push_tail(chrc->read_op->msgs, dbus_message_ref(msg)); return NULL; + } - async_dbus_op_free(op); + chrc->read_op = read_value(gatt, msg, chrc->value_handle, offset, + chrc_read_cb, chrc); + if (!chrc->read_op) + return btd_error_failed(msg, "Failed to send read request"); - return btd_error_failed(msg, "Failed to send read request"); + return NULL; } static bool chrc_write_complete(void *data) { struct characteristic *chrc = data; - chrc->write_id = 0; + chrc->write_op = NULL; /* * The characteristic might have been unregistered during the read. @@ -873,17 +913,24 @@ { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; + DBusMessageIter iter; uint8_t *value = NULL; - size_t value_len = 0; + int value_len = 0; bool supported = false; + uint16_t offset = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); - if (chrc->write_id) + if (chrc->write_op) return btd_error_in_progress(msg); - if (!parse_value_arg(msg, &value, &value_len)) + dbus_message_iter_init(msg, &iter); + + if (parse_value_arg(&iter, &value, &value_len)) + return btd_error_invalid_args(msg); + + if (parse_options(&iter, &offset)) return btd_error_invalid_args(msg); /* @@ -899,10 +946,10 @@ */ if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) { supported = true; - chrc->write_id = start_long_write(msg, chrc->value_handle, gatt, - true, value, value_len, + chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, + true, value, value_len, offset, chrc, chrc_write_complete); - if (chrc->write_id) + if (chrc->write_op) return NULL; } @@ -914,18 +961,18 @@ if (!mtu) return btd_error_failed(msg, "No ATT transport"); - if (value_len <= (unsigned) mtu - 3) - chrc->write_id = start_write_request(msg, + if (value_len <= mtu - 3 && !offset) + chrc->write_op = start_write_request(msg, chrc->value_handle, gatt, value, value_len, chrc, chrc_write_complete); else - chrc->write_id = start_long_write(msg, + chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, - false, value, value_len, + false, value, value_len, offset, chrc, chrc_write_complete); - if (chrc->write_id) + if (chrc->write_op) return NULL; } @@ -1074,26 +1121,17 @@ write_characteristic_cb, chrc); } -static DBusMessage *create_notify_reply(struct async_dbus_op *op, - bool success, uint8_t att_ecode) +static void create_notify_reply(struct async_dbus_op *op, bool success, + uint8_t att_ecode) { - DBusMessage *reply = NULL; - - if (!op->msg) - return NULL; + int err; if (success) - reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID); - else if (att_ecode) - reply = create_gatt_dbus_error(op->msg, att_ecode); + err = 0; else - reply = btd_error_failed(op->msg, - "Characteristic not available"); + err = att_ecode ? att_ecode : -ENOENT; - if (!reply) - error("Failed to construct D-Bus message reply"); - - return reply; + async_dbus_op_reply(op, err, NULL, 0); } static void register_notify_cb(uint16_t att_ecode, void *user_data) @@ -1101,16 +1139,15 @@ struct async_dbus_op *op = user_data; struct notify_client *client = op->data; struct characteristic *chrc = client->chrc; - DBusMessage *reply; if (att_ecode) { queue_remove(chrc->notify_clients, client); queue_remove(chrc->service->client->all_notify_clients, client); notify_client_free(client); - reply = create_notify_reply(op, false, att_ecode); + create_notify_reply(op, false, att_ecode); - goto done; + return; } if (!chrc->notifying) { @@ -1120,11 +1157,7 @@ "Notifying"); } - reply = create_notify_reply(op, true, 0); - -done: - if (reply) - g_dbus_send_message(btd_get_dbus_connection(), reply); + create_notify_reply(op, true, 0); } static DBusMessage *characteristic_start_notify(DBusConnection *conn, @@ -1174,9 +1207,7 @@ goto fail; } - op = new0(struct async_dbus_op, 1); - op->data = client; - op->msg = dbus_message_ref(msg); + op = async_dbus_op_new(msg, client); client->notify_id = bt_gatt_client_register_notify(gatt, chrc->value_handle, @@ -1205,9 +1236,6 @@ const char *sender = dbus_message_get_sender(msg); struct notify_client *client; - if (!chrc->notifying) - return btd_error_failed(msg, "Not notifying"); - client = queue_remove_if(chrc->notify_clients, match_notify_sender, (void *) sender); if (!client) @@ -1222,31 +1250,6 @@ return dbus_message_new_method_return(msg); } -static void append_desc_path(void *data, void *user_data) -{ - struct descriptor *desc = data; - DBusMessageIter *array = user_data; - - dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, - &desc->path); -} - -static gboolean characteristic_get_descriptors( - const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct characteristic *chrc = data; - DBusMessageIter array; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array); - - queue_foreach(chrc->descs, append_desc_path, &array); - - dbus_message_iter_close_container(iter, &array); - - return TRUE; -} - static const GDBusPropertyTable characteristic_properties[] = { { "UUID", "s", characteristic_get_uuid, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, @@ -1255,27 +1258,28 @@ { "Value", "ay", characteristic_get_value, NULL, characteristic_value_exists, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, - { "Notifying", "b", characteristic_get_notifying, NULL, NULL, + { "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 }, - { "Descriptors", "ao", characteristic_get_descriptors, NULL, NULL, - G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; static const GDBusMethodTable characteristic_methods[] = { - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL, - GDBUS_ARGS({ "value", "ay" }), - characteristic_read_value) }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", + GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + characteristic_read_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("WriteValue", - GDBUS_ARGS({ "value", "ay" }), - NULL, - characteristic_write_value) }, + GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, + characteristic_write_value) }, { GDBUS_EXPERIMENTAL_ASYNC_METHOD("StartNotify", NULL, NULL, - characteristic_start_notify) }, + characteristic_start_notify) }, { GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL, - characteristic_stop_notify) }, + characteristic_stop_notify) }, { } }; @@ -1305,7 +1309,9 @@ gatt_db_attribute_get_char_data(attr, &chrc->handle, &chrc->value_handle, - &chrc->props, &uuid); + &chrc->props, + &chrc->ext_props, + &uuid); chrc->attr = gatt_db_get_attribute(service->client->db, chrc->value_handle); @@ -1354,11 +1360,11 @@ DBG("Removing GATT characteristic: %s", chrc->path); - if (chrc->read_id) - bt_gatt_client_cancel(gatt, chrc->read_id); + if (chrc->read_op) + bt_gatt_client_cancel(gatt, chrc->read_op->id); - if (chrc->write_id) - bt_gatt_client_cancel(gatt, chrc->write_id); + if (chrc->write_op) + bt_gatt_client_cancel(gatt, chrc->write_op->id); queue_remove_all(chrc->notify_clients, NULL, NULL, remove_client); queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor); @@ -1404,31 +1410,6 @@ return TRUE; } -static void append_chrc_path(void *data, void *user_data) -{ - struct characteristic *chrc = data; - DBusMessageIter *array = user_data; - - dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, - &chrc->path); -} - -static gboolean service_get_characteristics(const GDBusPropertyTable *property, - DBusMessageIter *iter, void *data) -{ - struct service *service = data; - DBusMessageIter array; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array); - - if (service->chrcs_ready) - queue_foreach(service->chrcs, append_chrc_path, &array); - - dbus_message_iter_close_container(iter, &array); - - return TRUE; -} - static const GDBusPropertyTable service_properties[] = { { "UUID", "s", service_get_uuid, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, @@ -1436,8 +1417,6 @@ G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { "Primary", "b", service_get_primary, NULL, NULL, G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, - { "Characteristics", "ao", service_get_characteristics, NULL, NULL, - G_DBUS_PROPERTY_FLAG_EXPERIMENTAL }, { } }; @@ -1446,7 +1425,6 @@ struct service *service = data; queue_destroy(service->chrcs, NULL); /* List should be empty here */ - queue_destroy(service->pending_ext_props, NULL); g_free(service->path); free(service); } @@ -1460,7 +1438,6 @@ service = new0(struct service, 1); service->chrcs = queue_new(); - service->pending_ext_props = queue_new(); service->client = client; gatt_db_attribute_get_service_data(attr, &service->start_handle, @@ -1503,29 +1480,12 @@ DBG("Removing GATT service: %s", service->path); - if (service->idle_id) - g_source_remove(service->idle_id); - queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic); g_dbus_unregister_interface(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE); } -static void notify_chrcs(struct service *service) -{ - - if (service->chrcs_ready || - !queue_isempty(service->pending_ext_props)) - return; - - service->chrcs_ready = true; - - g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path, - GATT_SERVICE_IFACE, - "Characteristics"); -} - struct export_data { void *root; bool failed; @@ -1549,46 +1509,6 @@ queue_push_tail(charac->descs, desc); } -static void read_ext_props_cb(bool success, uint8_t att_ecode, - const uint8_t *value, uint16_t length, - void *user_data) -{ - struct characteristic *chrc = user_data; - struct service *service = chrc->service; - - if (!success) { - error("Failed to obtain extended properties - error: 0x%02x", - att_ecode); - return; - } - - if (!value || length != 2) { - error("Malformed extended properties value"); - return; - } - - chrc->ext_props = get_le16(value); - if (chrc->ext_props) - g_dbus_emit_property_changed(btd_get_dbus_connection(), - chrc->path, - GATT_CHARACTERISTIC_IFACE, - "Flags"); - - queue_remove(service->pending_ext_props, chrc); - - notify_chrcs(service); -} - -static void read_ext_props(void *data, void *user_data) -{ - struct characteristic *chrc = data; - - bt_gatt_client_read_value(chrc->service->client->gatt, - chrc->ext_props_handle, - read_ext_props_cb, - chrc, NULL); -} - static bool create_descriptors(struct gatt_db_attribute *attr, struct characteristic *charac) { @@ -1622,9 +1542,6 @@ queue_push_tail(service->chrcs, charac); - if (charac->ext_props_handle) - queue_push_tail(service->pending_ext_props, charac); - return; fail: @@ -1641,23 +1558,7 @@ gatt_db_service_foreach_char(attr, export_char, &data); - if (data.failed) - return false; - - /* Obtain extended properties */ - queue_foreach(service->pending_ext_props, read_ext_props, NULL); - - return true; -} - -static gboolean set_chrcs_ready(gpointer user_data) -{ - struct service *service = user_data; - - service->idle_id = 0; - notify_chrcs(service); - - return FALSE; + return !data.failed; } static void export_service(struct gatt_db_attribute *attr, void *user_data) @@ -1679,18 +1580,16 @@ } queue_push_tail(client->services, service); - - /* - * Asynchronously update the "Characteristics" property of the service. - * If there are any pending reads to obtain the value of the "Extended - * Properties" descriptor then wait until they are complete. - */ - if (!service->chrcs_ready && queue_isempty(service->pending_ext_props)) - service->idle_id = g_idle_add(set_chrcs_ready, service); } 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); @@ -1753,8 +1652,8 @@ DBG("Failed to re-register notification client"); - queue_remove(notify_client->chrc->notify_clients, client); - queue_remove(client->all_notify_clients, client); + queue_remove(notify_client->chrc->notify_clients, notify_client); + queue_remove(client->all_notify_clients, notify_client); notify_client_free(notify_client); } @@ -1765,8 +1664,14 @@ return; if (!client->gatt) { - error("GATT client not initialized"); - return; + struct bt_gatt_client *gatt; + + gatt = btd_device_get_gatt_client(client->device); + client->gatt = bt_gatt_client_clone(gatt); + if (!client->gatt) { + error("GATT client not initialized"); + return; + } } client->ready = true; @@ -1789,7 +1694,7 @@ DBG("Device connected."); bt_gatt_client_unref(client->gatt); - client->gatt = bt_gatt_client_ref(gatt); + client->gatt = bt_gatt_client_clone(gatt); /* * Services have already been created before. Re-enable notifications @@ -1840,47 +1745,6 @@ client->notify_id = 0; } -static void cancel_desc_ops(void *data, void *user_data) -{ - struct descriptor *desc = data; - struct bt_gatt_client *gatt = user_data; - - if (desc->read_id) { - bt_gatt_client_cancel(gatt, desc->read_id); - desc->read_id = 0; - } - - if (desc->write_id) { - bt_gatt_client_cancel(gatt, desc->write_id); - desc->write_id = 0; - } -} - -static void cancel_chrc_ops(void *data, void *user_data) -{ - struct characteristic *chrc = data; - struct bt_gatt_client *gatt = user_data; - - if (chrc->read_id) { - bt_gatt_client_cancel(gatt, chrc->read_id); - chrc->read_id = 0; - } - - if (chrc->write_id) { - bt_gatt_client_cancel(gatt, chrc->write_id); - chrc->write_id = 0; - } - - queue_foreach(chrc->descs, cancel_desc_ops, user_data); -} - -static void cancel_ops(void *data, void *user_data) -{ - struct service *service = data; - - queue_foreach(service->chrcs, cancel_chrc_ops, user_data); -} - void btd_gatt_client_disconnected(struct btd_gatt_client *client) { if (!client || !client->gatt) @@ -1894,7 +1758,6 @@ * done. */ queue_foreach(client->all_notify_clients, clear_notify_id, NULL); - queue_foreach(client->services, cancel_ops, client->gatt); bt_gatt_client_unref(client->gatt); client->gatt = NULL; diff -Nru bluez-5.37/src/gatt-database.c bluez-5.41/src/gatt-database.c --- bluez-5.37/src/gatt-database.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/gatt-database.c 2016-07-17 21:13:15.000000000 +0000 @@ -54,6 +54,7 @@ #endif #define GATT_MANAGER_IFACE "org.bluez.GattManager1" +#define GATT_PROFILE_IFACE "org.bluez.GattProfile1" #define GATT_SERVICE_IFACE "org.bluez.GattService1" #define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1" #define GATT_DESC_IFACE "org.bluez.GattDescriptor1" @@ -77,17 +78,25 @@ struct queue *ccc_callbacks; struct gatt_db_attribute *svc_chngd; struct gatt_db_attribute *svc_chngd_ccc; - struct queue *services; + struct queue *apps; struct queue *profiles; }; -struct external_service { +struct gatt_app { struct btd_gatt_database *database; - bool failed; char *owner; - char *path; /* Path to GattService1 */ + char *path; DBusMessage *reg; GDBusClient *client; + bool failed; + struct queue *profiles; + struct queue *services; + struct queue *proxies; +}; + +struct external_service { + struct gatt_app *app; + char *path; /* Path to GattService1 */ GDBusProxy *proxy; struct gatt_db_attribute *attrib; uint16_t attr_cnt; @@ -96,10 +105,8 @@ }; struct external_profile { - struct btd_gatt_database *database; - char *owner; - char *path; /* Path to GattProfile1 */ - unsigned int id; + struct gatt_app *app; + GDBusProxy *proxy; struct queue *profiles; /* btd_profile list */ }; @@ -109,6 +116,7 @@ GDBusProxy *proxy; uint8_t props; uint8_t ext_props; + uint32_t perm; struct gatt_db_attribute *attrib; struct gatt_db_attribute *ccc; struct queue *pending_reads; @@ -128,6 +136,7 @@ }; struct pending_op { + struct btd_device *device; unsigned int id; struct gatt_db_attribute *attrib; struct queue *owner_queue; @@ -342,21 +351,13 @@ queue_destroy(service->chrcs, chrc_free); queue_destroy(service->descs, desc_free); - gatt_db_remove_service(service->database->db, service->attrib); + if (service->attrib) + gatt_db_remove_service(service->app->database->db, + service->attrib); - if (service->client) { - g_dbus_client_set_disconnect_watch(service->client, NULL, NULL); - g_dbus_client_set_proxy_handlers(service->client, NULL, NULL, - NULL, NULL); - g_dbus_client_set_ready_watch(service->client, NULL, NULL); + if (service->app->client) g_dbus_proxy_unref(service->proxy); - g_dbus_client_unref(service->client); - } - if (service->reg) - dbus_message_unref(service->reg); - - g_free(service->owner); g_free(service->path); free(service); @@ -379,20 +380,10 @@ static void profile_release(struct external_profile *profile) { - DBusMessage *msg; - - if (!profile->id) - return; - - DBG("Releasing \"%s\"", profile->owner); + DBG("Releasing \"%s\"", profile->app->owner); - g_dbus_remove_watch(btd_get_dbus_connection(), profile->id); - - msg = dbus_message_new_method_call(profile->owner, profile->path, - "org.bluez.GattProfile1", - "Release"); - if (msg) - g_dbus_send_message(btd_get_dbus_connection(), msg); + g_dbus_proxy_method_call(profile->proxy, "Release", NULL, NULL, NULL, + NULL); } static void profile_free(void *data) @@ -403,12 +394,36 @@ profile_release(profile); - g_free(profile->owner); - g_free(profile->path); + g_dbus_proxy_unref(profile->proxy); free(profile); } +static void app_free(void *data) +{ + struct gatt_app *app = data; + + queue_destroy(app->profiles, profile_free); + queue_destroy(app->services, service_free); + queue_destroy(app->proxies, NULL); + + if (app->client) { + g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); + g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, + NULL, NULL); + g_dbus_client_set_ready_watch(app->client, NULL, NULL); + g_dbus_client_unref(app->client); + } + + if (app->reg) + dbus_message_unref(app->reg); + + g_free(app->owner); + g_free(app->path); + + free(app); +} + static void gatt_database_free(void *data) { struct btd_gatt_database *database = data; @@ -434,7 +449,7 @@ gatt_db_unregister(database->db, database->db_id); queue_destroy(database->device_states, device_state_free); - queue_destroy(database->services, service_free); + queue_destroy(database->apps, app_free); queue_destroy(database->profiles, profile_free); queue_destroy(database->ccc_callbacks, ccc_cb_free); database->device_states = NULL; @@ -453,8 +468,6 @@ uint8_t dst_type; bdaddr_t src, dst; - DBG("New incoming LE ATT connection"); - if (gerr) { error("%s", gerr->message); return; @@ -470,6 +483,9 @@ return; } + DBG("New incoming %s ATT connection", dst_type == BDADDR_BREDR ? + "BR/EDR" : "LE"); + adapter = adapter_find(&src); if (!adapter) return; @@ -1012,118 +1028,65 @@ const char *sender; }; -static bool match_service(const void *a, const void *b) +static bool match_app(const void *a, const void *b) { - const struct external_service *service = a; + const struct gatt_app *app = a; const struct svc_match_data *data = b; - return g_strcmp0(service->path, data->path) == 0 && - g_strcmp0(service->owner, data->sender) == 0; + return g_strcmp0(app->path, data->path) == 0 && + g_strcmp0(app->owner, data->sender) == 0; } -static gboolean service_free_idle_cb(void *data) +static gboolean app_free_idle_cb(void *data) { - service_free(data); + app_free(data); return FALSE; } -static void service_remove_helper(void *data) -{ - struct external_service *service = data; - - queue_remove(service->database->services, service); - - /* - * Do not run in the same loop, this may be a disconnect - * watch call and GDBusClient should not be destroyed. - */ - g_idle_add(service_free_idle_cb, service); -} - static void client_disconnect_cb(DBusConnection *conn, void *user_data) { + struct gatt_app *app = user_data; + struct btd_gatt_database *database = app->database; + DBG("Client disconnected"); - service_remove_helper(user_data); + if (queue_remove(database->apps, app)) + app_free(app); } -static void remove_service(void *data) +static void remove_app(void *data) { - struct external_service *service = data; + struct gatt_app *app = data; /* * Set callback to NULL to avoid potential race condition - * when calling remove_service and GDBusClient unref. + * when calling remove_app and GDBusClient unref. */ - g_dbus_client_set_disconnect_watch(service->client, NULL, NULL); + g_dbus_client_set_disconnect_watch(app->client, NULL, NULL); /* * Set proxy handlers to NULL, so that this gets called only once when * the first proxy that belongs to this service gets removed. */ - g_dbus_client_set_proxy_handlers(service->client, NULL, NULL, - NULL, NULL); - - service_remove_helper(service); -} - -static struct external_chrc *chrc_create(struct external_service *service, - GDBusProxy *proxy, - const char *path) -{ - struct external_chrc *chrc; - - chrc = new0(struct external_chrc, 1); - chrc->pending_reads = queue_new(); - chrc->pending_writes = queue_new(); - - chrc->path = g_strdup(path); - if (!chrc->path) { - queue_destroy(chrc->pending_reads, NULL); - queue_destroy(chrc->pending_writes, NULL); - free(chrc); - return NULL; - } - - chrc->service = service; - chrc->proxy = g_dbus_proxy_ref(proxy); - - return chrc; -} - -static struct external_desc *desc_create(struct external_service *service, - GDBusProxy *proxy, - const char *chrc_path) -{ - struct external_desc *desc; - - desc = new0(struct external_desc, 1); - desc->pending_reads = queue_new(); - desc->pending_writes = queue_new(); + g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL); - desc->chrc_path = g_strdup(chrc_path); - if (!desc->chrc_path) { - queue_destroy(desc->pending_reads, NULL); - queue_destroy(desc->pending_writes, NULL); - free(desc); - return NULL; - } - desc->service = service; - desc->proxy = g_dbus_proxy_ref(proxy); + queue_remove(app->database->apps, app); - return desc; + /* + * Do not run in the same loop, this may be a disconnect + * watch call and GDBusClient should not be destroyed. + */ + g_idle_add(app_free_idle_cb, app); } -static bool incr_attr_count(struct external_service *service, uint16_t incr) +static bool match_service_by_path(const void *a, const void *b) { - if (service->attr_cnt > UINT16_MAX - incr) - return false; - - service->attr_cnt += incr; + const struct external_service *service = a; + const char *path = b; - return true; + return strcmp(service->path, path) == 0; } static bool parse_path(GDBusProxy *proxy, const char *name, const char **path) @@ -1141,19 +1104,18 @@ return true; } -static bool check_service_path(GDBusProxy *proxy, - struct external_service *service) +static bool incr_attr_count(struct external_service *service, uint16_t incr) { - const char *service_path; - - if (!parse_path(proxy, "Service", &service_path)) + if (service->attr_cnt > UINT16_MAX - incr) return false; - return g_strcmp0(service_path, service->path) == 0; + service->attr_cnt += incr; + + return true; } static bool parse_chrc_flags(DBusMessageIter *array, uint8_t *props, - uint8_t *ext_props) + uint8_t *ext_props, uint32_t *perm) { const char *flag; @@ -1167,34 +1129,51 @@ if (!strcmp("broadcast", flag)) *props |= BT_GATT_CHRC_PROP_BROADCAST; - else if (!strcmp("read", flag)) + else if (!strcmp("read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; - else if (!strcmp("write-without-response", flag)) + *perm |= BT_ATT_PERM_READ; + } else if (!strcmp("write-without-response", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP; - else if (!strcmp("write", flag)) + *perm |= BT_ATT_PERM_WRITE; + } else if (!strcmp("write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; - else if (!strcmp("notify", flag)) + *perm |= BT_ATT_PERM_WRITE; + } else if (!strcmp("notify", flag)) { *props |= BT_GATT_CHRC_PROP_NOTIFY; - else if (!strcmp("indicate", flag)) + } else if (!strcmp("indicate", flag)) { *props |= BT_GATT_CHRC_PROP_INDICATE; - else if (!strcmp("authenticated-signed-writes", flag)) + } else if (!strcmp("authenticated-signed-writes", flag)) { *props |= BT_GATT_CHRC_PROP_AUTH; - else if (!strcmp("reliable-write", flag)) + *perm |= BT_ATT_PERM_WRITE; + } else if (!strcmp("reliable-write", flag)) { *ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE; - else if (!strcmp("writable-auxiliaries", flag)) + *perm |= BT_ATT_PERM_WRITE; + } else if (!strcmp("writable-auxiliaries", flag)) { *ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX; - else if (!strcmp("encrypt-read", flag)) { + } else if (!strcmp("encrypt-read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_READ; + *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_ENCRYPT; } else if (!strcmp("encrypt-write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *ext_props |= BT_GATT_CHRC_EXT_PROP_ENC_WRITE; + *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_ENCRYPT; } else if (!strcmp("encrypt-authenticated-read", flag)) { *props |= BT_GATT_CHRC_PROP_READ; *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ; + *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN; } else if (!strcmp("encrypt-authenticated-write", flag)) { *props |= BT_GATT_CHRC_PROP_WRITE; *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE; + *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN; + } else if (!strcmp("secure-read", flag)) { + *props |= BT_GATT_CHRC_PROP_READ; + *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_READ; + *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE; + } else if (!strcmp("secure-write", flag)) { + *props |= BT_GATT_CHRC_PROP_WRITE; + *ext_props |= BT_GATT_CHRC_EXT_PROP_AUTH_WRITE; + *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; } else { error("Invalid characteristic flag: %s", flag); return false; @@ -1231,6 +1210,10 @@ *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN; else if (!strcmp("encrypt-authenticated-write", flag)) *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN; + else if (!strcmp("secure-read", flag)) + *perm |= BT_ATT_PERM_READ | BT_ATT_PERM_READ_SECURE; + else if (!strcmp("secure-write", flag)) + *perm |= BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_SECURE; else { error("Invalid descriptor flag: %s", flag); return false; @@ -1244,6 +1227,7 @@ uint32_t *perm) { DBusMessageIter iter, array; + const char *iface; if (!g_dbus_proxy_get_property(proxy, "Flags", &iter)) return false; @@ -1253,170 +1237,254 @@ dbus_message_iter_recurse(&iter, &array); - if (perm) + iface = g_dbus_proxy_get_interface(proxy); + if (!strcmp(iface, GATT_DESC_IFACE)) return parse_desc_flags(&array, perm); - return parse_chrc_flags(&array, props, ext_props); + return parse_chrc_flags(&array, props, ext_props, perm); } -static void proxy_added_cb(GDBusProxy *proxy, void *user_data) +static struct external_chrc *chrc_create(struct gatt_app *app, + GDBusProxy *proxy, + const char *path) { - struct external_service *service = user_data; - const char *iface, *path; + struct external_service *service; + struct external_chrc *chrc; + const char *service_path; - if (service->failed || service->attrib) - return; + if (!parse_path(proxy, "Service", &service_path)) { + error("Failed to obtain service path for characteristic"); + return NULL; + } - iface = g_dbus_proxy_get_interface(proxy); - path = g_dbus_proxy_get_path(proxy); + service = queue_find(app->services, match_service_by_path, + service_path); + if (!service) { + error("Unable to find service for characteristic: %s", path); + return NULL; + } - if (!g_str_has_prefix(path, service->path)) - return; + chrc = new0(struct external_chrc, 1); + chrc->pending_reads = queue_new(); + chrc->pending_writes = queue_new(); - if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) { - if (service->proxy) - return; + chrc->path = g_strdup(path); + if (!chrc->path) + goto fail; - /* - * TODO: We may want to support adding included services in a - * single hierarchy. - */ - if (g_strcmp0(path, service->path) != 0) { - error("Multiple services added within hierarchy"); - service->failed = true; - return; - } + chrc->service = service; + chrc->proxy = g_dbus_proxy_ref(proxy); - /* Add 1 for the service declaration */ - if (!incr_attr_count(service, 1)) { - error("Failed to increment attribute count"); - service->failed = true; - return; - } + /* + * Add 2 for the characteristic declaration and the value + * attribute. + */ + if (!incr_attr_count(chrc->service, 2)) { + error("Failed to increment attribute count"); + goto fail; + } - service->proxy = g_dbus_proxy_ref(proxy); - } else if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) { - struct external_chrc *chrc; + /* + * Parse characteristic flags (i.e. properties) here since they + * are used to determine if any special descriptors should be + * created. + */ + if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, &chrc->perm)) { + error("Failed to parse characteristic properties"); + goto fail; + } - if (g_strcmp0(path, service->path) == 0) { - error("Characteristic path same as service path"); - service->failed = true; - return; - } + if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY || + chrc->props & BT_GATT_CHRC_PROP_INDICATE) && + !incr_attr_count(chrc->service, 1)) { + error("Failed to increment attribute count for CCC"); + goto fail; + } - chrc = chrc_create(service, proxy, path); - if (!chrc) { - service->failed = true; - return; - } + if (chrc->ext_props && !incr_attr_count(chrc->service, 1)) { + error("Failed to increment attribute count for CEP"); + goto fail; + } - /* - * Add 2 for the characteristic declaration and the value - * attribute. - */ - if (!incr_attr_count(service, 2)) { - error("Failed to increment attribute count"); - service->failed = true; - return; - } + queue_push_tail(chrc->service->chrcs, chrc); - /* - * Parse characteristic flags (i.e. properties) here since they - * are used to determine if any special descriptors should be - * created. - */ - if (!parse_flags(proxy, &chrc->props, &chrc->ext_props, NULL)) { - error("Failed to parse characteristic properties"); - service->failed = true; - return; - } + return chrc; - if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY || - chrc->props & BT_GATT_CHRC_PROP_INDICATE) && - !incr_attr_count(service, 1)) { - error("Failed to increment attribute count for CCC"); - service->failed = true; - return; - } +fail: + chrc_free(chrc); + return NULL; +} - if (chrc->ext_props && !incr_attr_count(service, 1)) { - error("Failed to increment attribute count for CEP"); - service->failed = true; - return; - } +static bool match_chrc(const void *a, const void *b) +{ + const struct external_chrc *chrc = a; + const char *path = b; - queue_push_tail(service->chrcs, chrc); - } else if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) { - struct external_desc *desc; - const char *chrc_path; + return strcmp(chrc->path, path) == 0; +} - if (!parse_path(proxy, "Characteristic", &chrc_path)) { - error("Failed to obtain characteristic path for " - "descriptor"); - service->failed = true; - return; - } +static bool match_service_by_chrc(const void *a, const void *b) +{ + const struct external_service *service = a; + const char *path = b; - desc = desc_create(service, proxy, chrc_path); - if (!desc) { - service->failed = true; - return; - } + return queue_find(service->chrcs, match_chrc, path); +} - /* Add 1 for the descriptor attribute */ - if (!incr_attr_count(service, 1)) { - error("Failed to increment attribute count"); - service->failed = true; - return; - } +static struct external_desc *desc_create(struct gatt_app *app, + GDBusProxy *proxy) +{ + struct external_service *service; + struct external_desc *desc; + const char *chrc_path; - /* - * Parse descriptors flags here since they are used to - * determine the permission the descriptor should have - */ - if (!parse_flags(proxy, NULL, NULL, &desc->perm)) { - error("Failed to parse characteristic properties"); - service->failed = true; - return; - } + if (!parse_path(proxy, "Characteristic", &chrc_path)) { + error("Failed to obtain characteristic path for descriptor"); + return NULL; + } - queue_push_tail(service->descs, desc); - } else { - DBG("Ignoring unrelated interface: %s", iface); - return; + service = queue_find(app->services, match_service_by_chrc, chrc_path); + if (!service) { + error("Unable to find service for characteristic: %s", + chrc_path); + return NULL; } - DBG("Object added to service - path: %s, iface: %s", path, iface); -} + desc = new0(struct external_desc, 1); + desc->pending_reads = queue_new(); + desc->pending_writes = queue_new(); -static void proxy_removed_cb(GDBusProxy *proxy, void *user_data) -{ - struct external_service *service = user_data; - const char *path; + desc->chrc_path = g_strdup(chrc_path); + if (!desc->chrc_path) + goto fail; - path = g_dbus_proxy_get_path(proxy); + desc->service = service; + desc->proxy = g_dbus_proxy_ref(proxy); - if (!g_str_has_prefix(path, service->path)) - return; + /* Add 1 for the descriptor attribute */ + if (!incr_attr_count(desc->service, 1)) { + error("Failed to increment attribute count"); + goto fail; + } - DBG("Proxy removed - removing service: %s", service->path); + /* + * Parse descriptors flags here since they are used to + * determine the permission the descriptor should have + */ + if (!parse_flags(proxy, NULL, NULL, &desc->perm)) { + error("Failed to parse characteristic properties"); + goto fail; + } - remove_service(service); + queue_push_tail(desc->service->descs, desc); + + return desc; + +fail: + desc_free(desc); + return NULL; } -static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid) +static bool check_service_path(GDBusProxy *proxy, + struct external_service *service) { - DBusMessageIter iter; - bt_uuid_t tmp; - const char *uuidstr; - - if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) - return false; + const char *service_path; - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + if (!parse_path(proxy, "Service", &service_path)) return false; - dbus_message_iter_get_basic(&iter, &uuidstr); + return g_strcmp0(service_path, service->path) == 0; +} + +static struct external_service *create_service(struct gatt_app *app, + GDBusProxy *proxy, + const char *path) +{ + struct external_service *service; + + if (!path || !g_str_has_prefix(path, "/")) + return NULL; + + service = queue_find(app->services, match_service_by_path, path); + if (service) { + error("Duplicated service: %s", path); + return NULL; + } + + service = new0(struct external_service, 1); + + service->app = app; + + service->path = g_strdup(path); + if (!service->path) + goto fail; + + service->proxy = g_dbus_proxy_ref(proxy); + service->chrcs = queue_new(); + service->descs = queue_new(); + + /* Add 1 for the service declaration */ + if (!incr_attr_count(service, 1)) { + error("Failed to increment attribute count"); + goto fail; + } + + queue_push_tail(app->services, service); + + return service; + +fail: + service_free(service); + return NULL; +} + +static void proxy_added_cb(GDBusProxy *proxy, void *user_data) +{ + struct gatt_app *app = user_data; + const char *iface, *path; + + if (app->failed) + return; + + queue_push_tail(app->proxies, proxy); + + iface = g_dbus_proxy_get_interface(proxy); + path = g_dbus_proxy_get_path(proxy); + + DBG("Object received: %s, iface: %s", path, iface); +} + +static void proxy_removed_cb(GDBusProxy *proxy, void *user_data) +{ + struct gatt_app *app = user_data; + struct external_service *service; + const char *path; + + path = g_dbus_proxy_get_path(proxy); + + service = queue_remove_if(app->services, match_service_by_path, + (void *) path); + if (!service) + return; + + DBG("Proxy removed - removing service: %s", service->path); + + service_free(service); +} + +static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid) +{ + DBusMessageIter iter; + bt_uuid_t tmp; + const char *uuidstr; + + if (!g_dbus_proxy_get_property(proxy, "UUID", &iter)) + return false; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return false; + + dbus_message_iter_get_basic(&iter, &uuidstr); if (bt_string_to_uuid(uuid, uuidstr) < 0) return false; @@ -1540,7 +1608,8 @@ free(op); } -static struct pending_op *pending_read_new(struct queue *owner_queue, +static struct pending_op *pending_read_new(struct btd_device *device, + struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id) { @@ -1549,6 +1618,7 @@ op = new0(struct pending_op, 1); op->owner_queue = owner_queue; + op->device = device; op->attrib = attrib; op->id = id; queue_push_tail(owner_queue, op); @@ -1556,33 +1626,75 @@ return op; } -static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy, - struct queue *owner_queue, - unsigned int id) +static void append_options(DBusMessageIter *iter, void *user_data) +{ + struct pending_op *op = user_data; + const char *path = device_get_path(op->device); + + dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path); +} + +static void read_setup_cb(DBusMessageIter *iter, void *user_data) +{ + struct pending_op *op = user_data; + DBusMessageIter dict; + + 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); + + append_options(&dict, op); + + dbus_message_iter_close_container(iter, &dict); +} + +static struct pending_op *send_read(struct btd_device *device, + struct gatt_db_attribute *attrib, + GDBusProxy *proxy, + struct queue *owner_queue, + unsigned int id) { struct pending_op *op; - uint8_t ecode = BT_ATT_ERROR_UNLIKELY; - op = pending_read_new(owner_queue, attrib, id); + op = pending_read_new(device, owner_queue, attrib, id); - if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb, - op, pending_op_free) == TRUE) - return; + if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb, + read_reply_cb, op, pending_op_free) == TRUE) + return op; pending_op_free(op); - gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0); + return NULL; } static void write_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; - DBusMessageIter array; + DBusMessageIter array, dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &op->data.iov_base, op->data.iov_len); dbus_message_iter_close_container(iter, &array); + + 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); + + append_options(&dict, op); + + dbus_message_iter_close_container(iter, &dict); + + if (!op->owner_queue) { + gatt_db_attribute_write_result(op->attrib, op->id, 0); + pending_op_free(op); + } } static void write_reply_cb(DBusMessage *message, void *user_data) @@ -1621,7 +1733,8 @@ gatt_db_attribute_write_result(op->attrib, op->id, ecode); } -static struct pending_op *pending_write_new(struct queue *owner_queue, +static struct pending_op *pending_write_new(struct btd_device *device, + struct queue *owner_queue, struct gatt_db_attribute *attrib, unsigned int id, const uint8_t *value, @@ -1634,6 +1747,7 @@ op->data.iov_base = (uint8_t *) value; op->data.iov_len = len; + op->device = device; op->owner_queue = owner_queue; op->attrib = attrib; op->id = id; @@ -1642,55 +1756,25 @@ return op; } -static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy, +static struct pending_op *send_write(struct btd_device *device, + struct gatt_db_attribute *attrib, + GDBusProxy *proxy, struct queue *owner_queue, unsigned int id, const uint8_t *value, size_t len) { struct pending_op *op; - uint8_t ecode = BT_ATT_ERROR_UNLIKELY; - op = pending_write_new(owner_queue, attrib, id, value, len); + op = pending_write_new(device, owner_queue, attrib, id, value, len); if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb, - write_reply_cb, op, - pending_op_free) == TRUE) - return; + owner_queue ? write_reply_cb : NULL, + op, pending_op_free) == TRUE) + return op; pending_op_free(op); - gatt_db_attribute_write_result(attrib, id, ecode); -} - -static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props) -{ - uint32_t perm = 0; - - if (props & BT_GATT_CHRC_PROP_WRITE || - props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP || - ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE || - ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE || - ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE) - perm |= BT_ATT_PERM_WRITE; - - if (props & BT_GATT_CHRC_PROP_READ || - ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ || - ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ) - perm |= BT_ATT_PERM_READ; - - if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_READ) - perm |= BT_ATT_PERM_READ_ENCRYPT; - - if (ext_props & BT_GATT_CHRC_EXT_PROP_ENC_WRITE) - perm |= BT_ATT_PERM_WRITE_ENCRYPT; - - if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_READ) - perm |= BT_ATT_PERM_READ_AUTHEN; - - if (ext_props & BT_GATT_CHRC_EXT_PROP_AUTH_WRITE) - perm |= BT_ATT_PERM_WRITE_AUTHEN; - - return perm; + return NULL; } static uint8_t ccc_write_cb(uint16_t value, void *user_data) @@ -1769,7 +1853,7 @@ len = MIN(BT_ATT_MAX_VALUE_LEN, len); value = len ? value : NULL; - send_notification_to_devices(chrc->service->database, + send_notification_to_devices(chrc->service->app->database, gatt_db_attribute_get_handle(chrc->attrib), value, len, gatt_db_attribute_get_handle(chrc->ccc), @@ -1783,7 +1867,7 @@ !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)) return true; - chrc->ccc = service_add_ccc(service->attrib, service->database, + chrc->ccc = service_add_ccc(service->attrib, service->app->database, ccc_write_cb, chrc, NULL); if (!chrc->ccc) { error("Failed to create CCC entry for characteristic"); @@ -1843,19 +1927,65 @@ return true; } +static struct btd_device *att_get_device(struct bt_att *att) +{ + GIOChannel *io = NULL; + GError *gerr = NULL; + bdaddr_t src, dst; + uint8_t dst_type; + struct btd_adapter *adapter; + + io = g_io_channel_unix_new(bt_att_get_fd(att)); + if (!io) + return NULL; + + bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + g_io_channel_unref(io); + return NULL; + } + + g_io_channel_unref(io); + + adapter = adapter_find(&src); + if (!adapter) { + error("Unable to find adapter object"); + return NULL; + } + + return btd_adapter_find_device(adapter, &dst, dst_type); +} + static void desc_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, void *user_data) { struct external_desc *desc = user_data; + struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; + } + + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; } - send_read(attrib, desc->proxy, desc->pending_reads, id); + if (send_read(device, attrib, desc->proxy, desc->pending_reads, id)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static void desc_write_cb(struct gatt_db_attribute *attrib, @@ -1865,13 +1995,26 @@ void *user_data) { struct external_desc *desc = user_data; + struct btd_device *device; if (desc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; } - send_write(attrib, desc->proxy, desc->pending_writes, id, value, len); + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; + } + + if (send_write(device, attrib, desc->proxy, desc->pending_writes, id, + value, len)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static bool database_add_desc(struct external_service *service, @@ -1904,13 +2047,25 @@ void *user_data) { struct external_chrc *chrc = user_data; + struct btd_device *device; if (chrc->attrib != attrib) { error("Read callback called with incorrect attribute"); - return; + goto fail; } - send_read(attrib, chrc->proxy, chrc->pending_reads, id); + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; + } + + if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static void chrc_write_cb(struct gatt_db_attribute *attrib, @@ -1920,20 +2075,37 @@ void *user_data) { struct external_chrc *chrc = user_data; + struct btd_device *device; + struct queue *queue; if (chrc->attrib != attrib) { error("Write callback called with incorrect attribute"); - return; + goto fail; + } + + device = att_get_device(att); + if (!device) { + error("Unable to find device object"); + goto fail; } - send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len); + if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) + queue = chrc->pending_writes; + else + queue = NULL; + + if (send_write(device, attrib, chrc->proxy, queue, id, value, len)) + return; + +fail: + gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY, + NULL, 0); } static bool database_add_chrc(struct external_service *service, struct external_chrc *chrc) { bt_uuid_t uuid; - uint32_t perm; const struct queue_entry *entry; if (!parse_uuid(chrc->proxy, &uuid)) { @@ -1946,14 +2118,8 @@ return false; } - /* - * TODO: Once shared/gatt-server properly supports permission checks, - * set the permissions based on a D-Bus property of the external - * characteristic. - */ - perm = permissions_from_props(chrc->props, chrc->ext_props); chrc->attrib = gatt_db_service_add_characteristic(service->attrib, - &uuid, perm, + &uuid, chrc->perm, chrc->props, chrc_read_cb, chrc_write_cb, chrc); if (!chrc->attrib) { @@ -1992,7 +2158,7 @@ return !desc->handled; } -static bool create_service_entry(struct external_service *service) +static bool database_add_service(struct external_service *service) { bt_uuid_t uuid; bool primary; @@ -2008,7 +2174,7 @@ return false; } - service->attrib = gatt_db_add_service(service->database->db, &uuid, + service->attrib = gatt_db_add_service(service->app->database->db, &uuid, primary, service->attr_cnt); if (!service->attrib) return false; @@ -2036,172 +2202,27 @@ return true; fail: - gatt_db_remove_service(service->database->db, service->attrib); + gatt_db_remove_service(service->app->database->db, service->attrib); service->attrib = NULL; return false; } -static void client_ready_cb(GDBusClient *client, void *user_data) +static bool database_add_app(struct gatt_app *app) { - struct external_service *service = user_data; - DBusMessage *reply; - bool fail = false; + const struct queue_entry *entry; - if (!service->proxy || service->failed) { - error("No valid external GATT objects found"); - fail = true; - reply = btd_error_failed(service->reg, - "No valid service object found"); - goto reply; - } + entry = queue_get_entries(app->services); + while (entry) { + if (!database_add_service(entry->data)) { + error("Failed to add service"); + return false; + } - if (!create_service_entry(service)) { - error("Failed to create GATT service entry in local database"); - fail = true; - reply = btd_error_failed(service->reg, - "Failed to create entry in database"); - goto reply; + entry = entry->next; } - DBG("GATT service registered: %s", service->path); - - reply = dbus_message_new_method_return(service->reg); - -reply: - g_dbus_send_message(btd_get_dbus_connection(), reply); - dbus_message_unref(service->reg); - service->reg = NULL; - - if (fail) - remove_service(service); -} - -static struct external_service *create_service(DBusConnection *conn, - DBusMessage *msg, const char *path) -{ - struct external_service *service; - const char *sender = dbus_message_get_sender(msg); - - if (!path || !g_str_has_prefix(path, "/")) - return NULL; - - service = new0(struct external_service, 1); - - service->client = g_dbus_client_new_full(conn, sender, path, path); - if (!service->client) - goto fail; - - service->owner = g_strdup(sender); - if (!service->owner) - goto fail; - - service->path = g_strdup(path); - if (!service->path) - goto fail; - - service->chrcs = queue_new(); - service->descs = queue_new(); - - service->reg = dbus_message_ref(msg); - - g_dbus_client_set_disconnect_watch(service->client, - client_disconnect_cb, service); - g_dbus_client_set_proxy_handlers(service->client, proxy_added_cb, - proxy_removed_cb, NULL, - service); - g_dbus_client_set_ready_watch(service->client, client_ready_cb, - service); - - return service; - -fail: - service_free(service); - return NULL; -} - -static DBusMessage *manager_register_service(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - struct btd_gatt_database *database = user_data; - const char *sender = dbus_message_get_sender(msg); - DBusMessageIter args; - const char *path; - struct external_service *service; - struct svc_match_data match_data; - - if (!dbus_message_iter_init(msg, &args)) - return btd_error_invalid_args(msg); - - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&args, &path); - - match_data.path = path; - match_data.sender = sender; - - if (queue_find(database->services, match_service, &match_data)) - return btd_error_already_exists(msg); - - dbus_message_iter_next(&args); - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) - return btd_error_invalid_args(msg); - - service = create_service(conn, msg, path); - if (!service) - return btd_error_failed(msg, "Failed to register service"); - - DBG("Registering service - path: %s", path); - - service->database = database; - queue_push_tail(database->services, service); - - return NULL; -} - -static DBusMessage *manager_unregister_service(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - struct btd_gatt_database *database = user_data; - const char *sender = dbus_message_get_sender(msg); - const char *path; - DBusMessageIter args; - struct external_service *service; - struct svc_match_data match_data; - - if (!dbus_message_iter_init(msg, &args)) - return btd_error_invalid_args(msg); - - if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&args, &path); - - match_data.path = path; - match_data.sender = sender; - - service = queue_remove_if(database->services, match_service, - &match_data); - if (!service) - return btd_error_does_not_exist(msg); - - service_free(service); - - return dbus_message_new_method_return(msg); -} - -static void profile_exited(DBusConnection *conn, void *user_data) -{ - struct external_profile *profile = user_data; - - DBG("\"%s\" exited", profile->owner); - - profile->id = 0; - - queue_remove(profile->database->profiles, profile); - - profile_free(profile); + return true; } static int profile_device_probe(struct btd_service *service) @@ -2227,8 +2248,8 @@ p = new0(struct btd_profile, 1); /* Assign directly to avoid having extra fields */ - p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner, - profile->path, uuid); + p->name = (const void *) g_strdup_printf("%s%s/%s", profile->app->owner, + g_dbus_proxy_get_path(profile->proxy), uuid); if (!p->name) { free(p); return -ENOMEM; @@ -2261,78 +2282,242 @@ adapter_add_profile(adapter, data); } -static int profile_create(DBusConnection *conn, - struct btd_gatt_database *database, - const char *sender, const char *path, - DBusMessageIter *iter) +static struct external_profile *create_profile(struct gatt_app *app, + GDBusProxy *proxy, + const char *path) { struct external_profile *profile; - DBusMessageIter uuids; + DBusMessageIter iter, array; if (!path || !g_str_has_prefix(path, "/")) - return -EINVAL; + return NULL; profile = new0(struct external_profile, 1); - profile->owner = g_strdup(sender); - if (!profile->owner) - goto fail; + profile->app = app; + profile->proxy = g_dbus_proxy_ref(proxy); + profile->profiles = queue_new(); - profile->path = g_strdup(path); - if (!profile->path) + if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) { + DBG("UUIDs property not found"); goto fail; + } - profile->profiles = queue_new(); - profile->database = database; - profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited, - profile, NULL); - - dbus_message_iter_recurse(iter, &uuids); + dbus_message_iter_recurse(&iter, &array); - while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) { + while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *uuid; - dbus_message_iter_get_basic(&uuids, &uuid); + dbus_message_iter_get_basic(&array, &uuid); if (profile_add(profile, uuid) < 0) goto fail; - dbus_message_iter_next(&uuids); + dbus_message_iter_next(&array); } if (queue_isempty(profile->profiles)) goto fail; - queue_foreach(profile->profiles, add_profile, database->adapter); - queue_push_tail(database->profiles, profile); + queue_foreach(profile->profiles, add_profile, app->database->adapter); + queue_push_tail(app->profiles, profile); - return 0; + return profile; fail: profile_free(profile); - return -EINVAL; + return NULL; } -static bool match_profile(const void *a, const void *b) +static void register_profile(void *data, void *user_data) { - const struct external_profile *profile = a; - const struct svc_match_data *data = b; + struct gatt_app *app = user_data; + GDBusProxy *proxy = data; + const char *iface = g_dbus_proxy_get_interface(proxy); + const char *path = g_dbus_proxy_get_path(proxy); + + if (app->failed) + return; + + if (g_strcmp0(iface, GATT_PROFILE_IFACE) == 0) { + struct external_profile *profile; + + profile = create_profile(app, proxy, path); + if (!profile) { + app->failed = true; + return; + } + } +} + +static void register_service(void *data, void *user_data) +{ + struct gatt_app *app = user_data; + GDBusProxy *proxy = data; + const char *iface = g_dbus_proxy_get_interface(proxy); + const char *path = g_dbus_proxy_get_path(proxy); + + if (app->failed) + return; + + if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) { + struct external_service *service; + + service = create_service(app, proxy, path); + if (!service) { + app->failed = true; + return; + } + } +} + +static void register_characteristic(void *data, void *user_data) +{ + struct gatt_app *app = user_data; + GDBusProxy *proxy = data; + const char *iface = g_dbus_proxy_get_interface(proxy); + const char *path = g_dbus_proxy_get_path(proxy); + + if (app->failed) + return; + + iface = g_dbus_proxy_get_interface(proxy); + path = g_dbus_proxy_get_path(proxy); + + if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) { + struct external_chrc *chrc; + + chrc = chrc_create(app, proxy, path); + if (!chrc) { + app->failed = true; + return; + } + } +} + +static void register_descriptor(void *data, void *user_data) +{ + struct gatt_app *app = user_data; + GDBusProxy *proxy = data; + const char *iface = g_dbus_proxy_get_interface(proxy); + + if (app->failed) + return; + + if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) { + struct external_desc *desc; + + desc = desc_create(app, proxy); + if (!desc) { + app->failed = true; + return; + } + } +} + +static void client_ready_cb(GDBusClient *client, void *user_data) +{ + struct gatt_app *app = user_data; + DBusMessage *reply; + bool fail = false; - return g_strcmp0(profile->path, data->path) == 0 && - g_strcmp0(profile->owner, data->sender) == 0; + /* + * Process received objects + */ + if (queue_isempty(app->proxies)) { + error("No object received"); + fail = true; + reply = btd_error_failed(app->reg, + "No object received"); + goto reply; + } + + queue_foreach(app->proxies, register_profile, app); + queue_foreach(app->proxies, register_service, app); + queue_foreach(app->proxies, register_characteristic, app); + queue_foreach(app->proxies, register_descriptor, app); + + if ((queue_isempty(app->services) && queue_isempty(app->profiles)) || + app->failed) { + error("No valid external GATT objects found"); + fail = true; + reply = btd_error_failed(app->reg, + "No valid service object found"); + goto reply; + } + + if (!database_add_app(app)) { + error("Failed to create GATT service entry in local database"); + fail = true; + reply = btd_error_failed(app->reg, + "Failed to create entry in database"); + goto reply; + } + + DBG("GATT application registered: %s:%s", app->owner, app->path); + + reply = dbus_message_new_method_return(app->reg); + +reply: + g_dbus_send_message(btd_get_dbus_connection(), reply); + dbus_message_unref(app->reg); + app->reg = NULL; + + if (fail) + remove_app(app); +} + +static struct gatt_app *create_app(DBusConnection *conn, DBusMessage *msg, + const char *path) +{ + struct gatt_app *app; + const char *sender = dbus_message_get_sender(msg); + + if (!path || !g_str_has_prefix(path, "/")) + return NULL; + + app = new0(struct gatt_app, 1); + + app->client = g_dbus_client_new_full(conn, sender, path, path); + if (!app->client) + goto fail; + + app->owner = g_strdup(sender); + if (!app->owner) + goto fail; + + app->path = g_strdup(path); + if (!app->path) + goto fail; + + app->services = queue_new(); + app->profiles = queue_new(); + app->proxies = queue_new(); + app->reg = dbus_message_ref(msg); + + g_dbus_client_set_disconnect_watch(app->client, client_disconnect_cb, + app); + g_dbus_client_set_proxy_handlers(app->client, proxy_added_cb, + proxy_removed_cb, NULL, app); + g_dbus_client_set_ready_watch(app->client, client_ready_cb, app); + + return app; + +fail: + app_free(app); + return NULL; } -static DBusMessage *manager_register_profile(DBusConnection *conn, +static DBusMessage *manager_register_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_gatt_database *database = user_data; const char *sender = dbus_message_get_sender(msg); DBusMessageIter args; const char *path; + struct gatt_app *app; struct svc_match_data match_data; - DBG("sender %s", sender); - if (!dbus_message_iter_init(msg, &args)) return btd_error_invalid_args(msg); @@ -2344,27 +2529,33 @@ match_data.path = path; match_data.sender = sender; - if (queue_find(database->profiles, match_profile, &match_data)) + if (queue_find(database->apps, match_app, &match_data)) return btd_error_already_exists(msg); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) return btd_error_invalid_args(msg); - if (profile_create(conn, database, sender, path, &args) < 0) - return btd_error_failed(msg, "Failed to register profile"); + app = create_app(conn, msg, path); + if (!app) + return btd_error_failed(msg, "Failed to register application"); - return dbus_message_new_method_return(msg); + DBG("Registering application: %s:%s", sender, path); + + app->database = database; + queue_push_tail(database->apps, app); + + return NULL; } -static DBusMessage *manager_unregister_profile(DBusConnection *conn, +static DBusMessage *manager_unregister_app(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct btd_gatt_database *database = user_data; const char *sender = dbus_message_get_sender(msg); const char *path; DBusMessageIter args; - struct external_profile *profile; + struct gatt_app *app; struct svc_match_data match_data; if (!dbus_message_iter_init(msg, &args)) @@ -2378,30 +2569,23 @@ match_data.path = path; match_data.sender = sender; - profile = queue_remove_if(database->profiles, match_profile, - &match_data); - if (!profile) + app = queue_remove_if(database->apps, match_app, &match_data); + if (!app) return btd_error_does_not_exist(msg); - profile_free(profile); + app_free(app); return dbus_message_new_method_return(msg); } static const GDBusMethodTable manager_methods[] = { - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService", - GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }), - NULL, manager_register_service) }, - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterService", - GDBUS_ARGS({ "service", "o" }), - NULL, manager_unregister_service) }, - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile", - GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterApplication", + GDBUS_ARGS({ "application", "o" }, { "options", "a{sv}" }), NULL, - manager_register_profile) }, - { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile", - GDBUS_ARGS({ "profile", "o" }), - NULL, manager_unregister_profile) }, + manager_register_app) }, + { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterApplication", + GDBUS_ARGS({ "application", "o" }), + NULL, manager_unregister_app) }, { } }; @@ -2418,7 +2602,7 @@ database->adapter = btd_adapter_ref(adapter); database->db = gatt_db_new(); database->device_states = queue_new(); - database->services = queue_new(); + database->apps = queue_new(); database->profiles = queue_new(); database->ccc_callbacks = queue_new(); diff -Nru bluez-5.37/src/profile.c bluez-5.41/src/profile.c --- bluez-5.37/src/profile.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/profile.c 2016-05-26 16:51:11.000000000 +0000 @@ -1047,10 +1047,11 @@ conn); } - if (conn->service && service_accept(conn->service) == 0) { - if (send_new_connection(ext, conn)) - return; - } + if (conn->service && service_accept(conn->service) < 0) + goto drop; + + if (send_new_connection(ext, conn)) + return; drop: if (conn->service) diff -Nru bluez-5.37/src/service.c bluez-5.41/src/service.c --- bluez-5.37/src/service.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/service.c 2016-07-17 21:13:15.000000000 +0000 @@ -353,7 +353,8 @@ void btd_service_connecting_complete(struct btd_service *service, int err) { - if (service->state != BTD_SERVICE_STATE_CONNECTING) + if (service->state != BTD_SERVICE_STATE_DISCONNECTED && + service->state != BTD_SERVICE_STATE_CONNECTING) return; if (err == 0) diff -Nru bluez-5.37/src/shared/att.c bluez-5.41/src/shared/att.c --- bluez-5.37/src/shared/att.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/att.c 2016-07-17 21:13:15.000000000 +0000 @@ -34,6 +34,7 @@ #include "src/shared/util.h" #include "src/shared/timeout.h" #include "lib/bluetooth.h" +#include "lib/l2cap.h" #include "lib/uuid.h" #include "src/shared/att.h" #include "src/shared/crypto.h" @@ -184,7 +185,7 @@ unsigned int id; unsigned int timeout_id; enum att_op_type type; - uint16_t opcode; + uint8_t opcode; void *pdu; uint16_t len; bt_att_response_func_t callback; @@ -535,6 +536,16 @@ disconn->callback(err, disconn->user_data); } +static void disc_att_send_op(void *data) +{ + struct att_send_op *op = data; + + if (op->callback) + op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); + + destroy_att_send_op(op); +} + static bool disconnect_cb(struct io *io, void *user_data) { struct bt_att *att = user_data; @@ -557,7 +568,20 @@ io_destroy(att->io); att->io = NULL; - bt_att_cancel_all(att); + /* Notify request callbacks */ + queue_remove_all(att->req_queue, NULL, NULL, disc_att_send_op); + queue_remove_all(att->ind_queue, NULL, NULL, disc_att_send_op); + queue_remove_all(att->write_queue, NULL, NULL, disc_att_send_op); + + if (att->pending_req) { + disc_att_send_op(att->pending_req); + att->pending_req = NULL; + } + + if (att->pending_ind) { + disc_att_send_op(att->pending_ind); + att->pending_ind = NULL; + } bt_att_ref(att); @@ -573,10 +597,11 @@ { int security; - security = bt_att_get_security(att); - if (security != BT_ATT_SECURITY_AUTO) + if (att->io_sec_level != BT_ATT_SECURITY_AUTO) return false; + security = bt_att_get_security(att); + if (ecode == BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION && security < BT_ATT_SECURITY_MEDIUM) security = BT_ATT_SECURITY_MEDIUM; @@ -943,6 +968,18 @@ free(att); } +static uint16_t get_l2cap_mtu(int fd) +{ + socklen_t len; + struct l2cap_options l2o; + + len = sizeof(l2o); + if (getsockopt(fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) + return 0; + + return l2o.omtu; +} + struct bt_att *bt_att_new(int fd, bool ext_signed) { struct bt_att *att; @@ -952,10 +989,6 @@ att = new0(struct bt_att, 1); att->fd = fd; - att->mtu = BT_ATT_DEFAULT_LE_MTU; - att->buf = malloc(att->mtu); - if (!att->buf) - goto fail; att->io = io_new(fd); if (!att->io) @@ -979,7 +1012,19 @@ att->io_on_l2cap = is_io_l2cap_based(att->fd); if (!att->io_on_l2cap) - att->io_sec_level = BT_SECURITY_LOW; + att->io_sec_level = BT_ATT_SECURITY_LOW; + + if (bt_att_get_link_type(att) == BT_ATT_LINK_BREDR) + att->mtu = get_l2cap_mtu(att->fd); + else + att->mtu = BT_ATT_DEFAULT_LE_MTU; + + if (att->mtu < BT_ATT_DEFAULT_LE_MTU) + goto fail; + + att->buf = malloc(att->mtu); + if (!att->buf) + goto fail; return bt_att_ref(att); @@ -1075,6 +1120,28 @@ return true; } +uint8_t bt_att_get_link_type(struct bt_att *att) +{ + struct sockaddr_l2 src; + socklen_t len; + + if (!att) + return -EINVAL; + + if (!att->io_on_l2cap) + return BT_ATT_LINK_LOCAL; + + len = sizeof(src); + memset(&src, 0, len); + if (getsockname(att->fd, (void *)&src, &len) < 0) + return -errno; + + if (src.l2_bdaddr_type == BDADDR_BREDR) + return BT_ATT_LINK_BREDR; + + return BT_ATT_LINK_LE; +} + bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback, void *user_data, bt_att_destroy_func_t destroy) diff -Nru bluez-5.37/src/shared/att.h bluez-5.41/src/shared/att.h --- bluez-5.37/src/shared/att.h 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/src/shared/att.h 2016-07-17 21:13:15.000000000 +0000 @@ -53,6 +53,7 @@ uint16_t bt_att_get_mtu(struct bt_att *att); bool bt_att_set_mtu(struct bt_att *att, uint16_t mtu); +uint8_t bt_att_get_link_type(struct bt_att *att); bool bt_att_set_timeout_cb(struct bt_att *att, bt_att_timeout_func_t callback, void *user_data, diff -Nru bluez-5.37/src/shared/att-types.h bluez-5.41/src/shared/att-types.h --- bluez-5.37/src/shared/att-types.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/att-types.h 2016-07-17 21:13:15.000000000 +0000 @@ -31,11 +31,16 @@ #define BT_ATT_SECURITY_LOW 1 #define BT_ATT_SECURITY_MEDIUM 2 #define BT_ATT_SECURITY_HIGH 3 +#define BT_ATT_SECURITY_FIPS 4 #define BT_ATT_DEFAULT_LE_MTU 23 #define BT_ATT_MAX_LE_MTU 517 #define BT_ATT_MAX_VALUE_LEN 512 +#define BT_ATT_LINK_BREDR 0x00 +#define BT_ATT_LINK_LE 0x01 +#define BT_ATT_LINK_LOCAL 0xff + /* ATT protocol opcodes */ #define BT_ATT_OP_ERROR_RSP 0x01 #define BT_ATT_OP_MTU_REQ 0x02 @@ -123,6 +128,10 @@ BT_ATT_PERM_WRITE_AUTHEN) #define BT_ATT_PERM_AUTHOR 0x40 #define BT_ATT_PERM_NONE 0x80 +#define BT_ATT_PERM_READ_SECURE 0x0100 +#define BT_ATT_PERM_WRITE_SECURE 0x0200 +#define BT_ATT_PERM_SECURE (BT_ATT_PERM_READ_SECURE | \ + BT_ATT_PERM_WRITE_SECURE) /* GATT Characteristic Properties Bitfield values */ #define BT_GATT_CHRC_PROP_BROADCAST 0x01 diff -Nru bluez-5.37/src/shared/btsnoop.c bluez-5.41/src/shared/btsnoop.c --- bluez-5.37/src/shared/btsnoop.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/btsnoop.c 2016-05-26 16:51:11.000000000 +0000 @@ -198,7 +198,8 @@ } bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, - uint32_t flags, const void *data, uint16_t size) + uint32_t flags, uint32_t drops, const void *data, + uint16_t size) { struct btsnoop_pkt pkt; uint64_t ts; @@ -212,7 +213,7 @@ pkt.size = htobe32(size); pkt.len = htobe32(size); pkt.flags = htobe32(flags); - pkt.drops = htobe32(0); + pkt.drops = htobe32(drops); pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); @@ -254,8 +255,8 @@ } bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, - uint16_t index, uint16_t opcode, - const void *data, uint16_t size) + uint16_t index, uint16_t opcode, uint32_t drops, + const void *data, uint16_t size) { uint32_t flags; @@ -283,7 +284,7 @@ return false; } - return btsnoop_write(btsnoop, tv, flags, data, size); + return btsnoop_write(btsnoop, tv, flags, drops, data, size); } bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, @@ -303,7 +304,7 @@ return false; } - return btsnoop_write(btsnoop, tv, flags, data, size); + return btsnoop_write(btsnoop, tv, flags, 0, data, size); } static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv, diff -Nru bluez-5.37/src/shared/btsnoop.h bluez-5.41/src/shared/btsnoop.h --- bluez-5.37/src/shared/btsnoop.h 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/btsnoop.h 2016-05-26 16:51:11.000000000 +0000 @@ -62,6 +62,8 @@ #define BTSNOOP_BUS_RS232 4 #define BTSNOOP_BUS_PCI 5 #define BTSNOOP_BUS_SDIO 6 +#define BTSNOOP_BUS_SPI 7 +#define BTSNOOP_BUS_I2C 8 struct btsnoop_opcode_new_index { uint8_t type; @@ -99,11 +101,11 @@ uint32_t btsnoop_get_format(struct btsnoop *btsnoop); -bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, - uint32_t flags, const void *data, uint16_t size); +bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, uint32_t flags, + uint32_t drops, const void *data, uint16_t size); bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv, - uint16_t index, uint16_t opcode, - const void *data, uint16_t size); + uint16_t index, uint16_t opcode, uint32_t drops, + const void *data, uint16_t size); bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv, uint16_t frequency, const void *data, uint16_t size); diff -Nru bluez-5.37/src/shared/crypto.c bluez-5.41/src/shared/crypto.c --- bluez-5.37/src/shared/crypto.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/crypto.c 2016-07-17 21:13:15.000000000 +0000 @@ -568,8 +568,8 @@ return bt_crypto_e(crypto, k, res, res); } -static bool aes_cmac(struct bt_crypto *crypto, uint8_t key[16], uint8_t *msg, - size_t msg_len, uint8_t res[16]) +static bool aes_cmac(struct bt_crypto *crypto, const uint8_t key[16], + const uint8_t *msg, size_t msg_len, uint8_t res[16]) { uint8_t key_msb[16], out[16], msg_msb[CMAC_MSG_MAX]; ssize_t len; @@ -679,3 +679,12 @@ return true; } + +bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16], + const uint8_t keyid[4], uint8_t res[16]) +{ + if (!aes_cmac(crypto, w, keyid, 4, res)) + return false; + + return true; +} diff -Nru bluez-5.37/src/shared/crypto.h bluez-5.41/src/shared/crypto.h --- bluez-5.37/src/shared/crypto.h 2014-12-12 13:38:33.000000000 +0000 +++ bluez-5.41/src/shared/crypto.h 2016-07-17 21:13:15.000000000 +0000 @@ -56,6 +56,8 @@ uint8_t a1[7], uint8_t a2[7], uint8_t res[16]); bool bt_crypto_g2(struct bt_crypto *crypto, uint8_t u[32], uint8_t v[32], uint8_t x[16], uint8_t y[16], uint32_t *val); +bool bt_crypto_h6(struct bt_crypto *crypto, const uint8_t w[16], + const uint8_t keyid[4], uint8_t res[16]); bool bt_crypto_sign_att(struct bt_crypto *crypto, const uint8_t key[16], const uint8_t *m, uint16_t m_len, uint32_t sign_cnt, uint8_t signature[12]); diff -Nru bluez-5.37/src/shared/gatt-client.c bluez-5.41/src/shared/gatt-client.c --- bluez-5.37/src/shared/gatt-client.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/gatt-client.c 2016-07-17 21:13:15.000000000 +0000 @@ -55,6 +55,9 @@ struct bt_att *att; int ref_count; + struct bt_gatt_client *parent; + struct queue *clones; + bt_gatt_client_callback_t ready_callback; bt_gatt_client_destroy_func_t ready_destroy; void *ready_data; @@ -240,9 +243,9 @@ if (bt_uuid_cmp(&uuid, gatt_db_attribute_get_type(attr))) return NULL; - if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, - &properties, NULL)) - return NULL; + if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, &properties, + NULL, NULL)) + return NULL; chrc = new0(struct notify_chrc, 1); @@ -291,25 +294,6 @@ uint16_t end; }; -static bool match_notify_data_handle_range(const void *a, const void *b) -{ - const struct notify_data *notify_data = a; - struct notify_chrc *chrc = notify_data->chrc; - const struct handle_range *range = b; - - return chrc->value_handle >= range->start && - chrc->value_handle <= range->end; -} - -static bool match_notify_chrc_handle_range(const void *a, const void *b) -{ - const struct notify_chrc *chrc = a; - const struct handle_range *range = b; - - return chrc->value_handle >= range->start && - chrc->value_handle <= range->end; -} - static void notify_data_cleanup(void *data) { struct notify_data *notify_data = data; @@ -320,32 +304,6 @@ notify_data_unref(notify_data); } -static void gatt_client_remove_all_notify_in_range( - struct bt_gatt_client *client, - uint16_t start_handle, uint16_t end_handle) -{ - struct handle_range range; - - range.start = start_handle; - range.end = end_handle; - - queue_remove_all(client->notify_list, match_notify_data_handle_range, - &range, notify_data_cleanup); -} - -static void gatt_client_remove_notify_chrcs_in_range( - struct bt_gatt_client *client, - uint16_t start_handle, uint16_t end_handle) -{ - struct handle_range range; - - range.start = start_handle; - range.end = end_handle; - - queue_remove_all(client->notify_chrcs, match_notify_chrc_handle_range, - &range, notify_chrc_free); -} - struct discovery_op; typedef void (*discovery_op_complete_func_t)(struct discovery_op *op, @@ -357,11 +315,13 @@ struct bt_gatt_client *client; struct queue *pending_svcs; struct queue *pending_chrcs; - struct queue *tmp_queue; + struct queue *svcs; + struct queue *ext_prop_desc; struct gatt_db_attribute *cur_svc; bool success; uint16_t start; uint16_t end; + uint16_t last; int ref_count; discovery_op_complete_func_t complete_func; discovery_op_fail_func_t failure_func; @@ -371,10 +331,26 @@ { queue_destroy(op->pending_svcs, NULL); queue_destroy(op->pending_chrcs, free); - queue_destroy(op->tmp_queue, NULL); + queue_destroy(op->svcs, NULL); + queue_destroy(op->ext_prop_desc, NULL); free(op); } +static void discovery_op_complete(struct discovery_op *op, bool success, + uint8_t err) +{ + /* Reset remaining range */ + if (success) { + if (op->last != UINT16_MAX) + gatt_db_clear_range(op->client->db, op->last + 1, + UINT16_MAX); + } else + gatt_db_clear(op->client->db); + + op->success = success; + op->complete_func(op, success, err); +} + static struct discovery_op *discovery_op_create(struct bt_gatt_client *client, uint16_t start, uint16_t end, discovery_op_complete_func_t complete_func, @@ -385,7 +361,8 @@ op = new0(struct discovery_op, 1); op->pending_svcs = queue_new(); op->pending_chrcs = queue_new(); - op->tmp_queue = queue_new(); + op->svcs = queue_new(); + op->ext_prop_desc = queue_new(); op->client = client; op->complete_func = complete_func; op->failure_func = failure_func; @@ -501,17 +478,11 @@ /* Move on to the next service */ attr = queue_pop_head(op->pending_svcs); if (!attr) { - struct queue *tmp_queue; - - tmp_queue = op->pending_svcs; - op->pending_svcs = op->tmp_queue; - op->tmp_queue = tmp_queue; - /* * We have processed all include definitions. Move on to * characteristics. */ - attr = queue_pop_head(op->pending_svcs); + attr = queue_pop_head(op->svcs); if (!attr) goto failed; @@ -535,7 +506,7 @@ goto failed; } - queue_push_tail(op->tmp_queue, attr); + queue_push_tail(op->svcs, attr); op->cur_svc = attr; if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) goto failed; @@ -556,8 +527,7 @@ discovery_op_unref(op); failed: - op->success = false; - op->complete_func(op, false, att_ecode); + discovery_op_complete(op, false, att_ecode); } struct chrc { @@ -588,8 +558,12 @@ chrc_data->properties, NULL, NULL, NULL); - if (!attr) + if (!attr) { + util_debug(client->debug_callback, client->debug_data, + "Failed to insert characteristic at 0x%04x", + chrc_data->value_handle); goto failed; + } if (gatt_db_attribute_get_handle(attr) != chrc_data->value_handle) @@ -633,6 +607,107 @@ return false; } +static void ext_prop_write_cb(struct gatt_db_attribute *attrib, + int err, void *user_data) +{ + struct bt_gatt_client *client = user_data; + + util_debug(client->debug_callback, client->debug_data, + "Value set status: %d", err); +} + +static void ext_prop_read_cb(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data); + +static bool read_ext_prop_desc(struct discovery_op *op) +{ + struct bt_gatt_client *client = op->client; + uint16_t handle; + struct gatt_db_attribute *attr; + + attr = queue_peek_head(op->ext_prop_desc); + if (!attr) + return false; + + handle = gatt_db_attribute_get_handle(attr); + bt_gatt_client_read_value(client, handle, ext_prop_read_cb, + discovery_op_ref(op), + discovery_op_unref); + + return true; +} + +static void ext_prop_read_cb(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct discovery_op *op = user_data; + struct bt_gatt_client *client = op->client; + bool discovering; + struct gatt_db_attribute *desc_attr = NULL; + struct gatt_db_attribute *next_srv; + uint16_t start, end; + + util_debug(client->debug_callback, client->debug_data, + "Ext. prop value: 0x%04x", (uint16_t)value[0]); + + desc_attr = queue_pop_head(op->ext_prop_desc); + if (!desc_attr) + goto failed; + + if (!gatt_db_attribute_write(desc_attr, 0, value, length, 0, NULL, + ext_prop_write_cb, client)) + goto failed; + + /* Any other descriptor to read? */ + if (read_ext_prop_desc(op)) + return; + + /* Continue with discovery */ + do { + if (!discover_descs(op, &discovering)) + goto failed; + + if (discovering) + return; + + /* Done with the current service */ + gatt_db_service_set_active(op->cur_svc, true); + + next_srv = queue_pop_head(op->svcs); + if (!next_srv) + goto done; + + if (!gatt_db_attribute_get_service_handles(next_srv, &start, + &end)) + goto failed; + + } while (start == end); + + /* Move on to the next service */ + op->cur_svc = next_srv; + + client->discovery_req = bt_gatt_discover_characteristics(client->att, + start, end, + discover_chrcs_cb, + discovery_op_ref(op), + discovery_op_unref); + if (client->discovery_req) + return; + + util_debug(client->debug_callback, client->debug_data, + "Failed to start characteristic discovery"); + + discovery_op_unref(op); + +failed: + success = false; + +done: + discovery_op_complete(op, success, att_ecode); +} + static void discover_descs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) @@ -647,6 +722,7 @@ char uuid_str[MAX_LEN_UUID_STR]; unsigned int desc_count; bool discovering; + bt_uuid_t ext_prop_uuid; discovery_req_clear(client); @@ -669,6 +745,8 @@ util_debug(client->debug_callback, client->debug_data, "Descriptors found: %u", desc_count); + bt_uuid16_create(&ext_prop_uuid, GATT_CHARAC_EXT_PROPER_UUID); + while (bt_gatt_iter_next_descriptor(&iter, &handle, u128.data)) { bt_uuid128_create(&uuid, u128); @@ -686,8 +764,15 @@ if (gatt_db_attribute_get_handle(attr) != handle) goto failed; + + if (!bt_uuid_cmp(&ext_prop_uuid, &uuid)) + queue_push_tail(op->ext_prop_desc, attr); } + /* If we got extended prop descriptor, lets read it right away */ + if (read_ext_prop_desc(op)) + return; + next: if (!discover_descs(op, &discovering)) goto failed; @@ -698,7 +783,7 @@ /* Done with the current service */ gatt_db_service_set_active(op->cur_svc, true); - attr = queue_pop_head(op->pending_svcs); + attr = queue_pop_head(op->svcs); if (!attr) goto done; @@ -727,8 +812,7 @@ success = false; done: - op->success = success; - op->complete_func(op, success, att_ecode); + discovery_op_complete(op, success, att_ecode); } static void discover_chrcs_cb(bool success, uint8_t att_ecode, @@ -805,7 +889,7 @@ /* Done with the current service */ gatt_db_service_set_active(op->cur_svc, true); - attr = queue_pop_head(op->pending_svcs); + attr = queue_pop_head(op->svcs); if (!attr) goto done; @@ -834,8 +918,7 @@ success = false; done: - op->success = success; - op->complete_func(op, success, att_ecode); + discovery_op_complete(op, success, att_ecode); } static void discover_secondary_cb(bool success, uint8_t att_ecode, @@ -903,6 +986,10 @@ /* Skip if service already active */ if (!gatt_db_service_get_active(attr)) queue_push_tail(op->pending_svcs, attr); + + /* Update last handle */ + if (end > op->last) + op->last = end; } next: @@ -916,10 +1003,10 @@ } /* - * Store the service in the tmp queue to be reused during + * Store the service in the svcs queue to be reused during * characteristics discovery later. */ - queue_push_tail(op->tmp_queue, attr); + queue_push_tail(op->svcs, attr); op->cur_svc = attr; if (!gatt_db_attribute_get_service_handles(attr, &start, &end)) { @@ -940,8 +1027,7 @@ discovery_op_unref(op); done: - op->success = success; - op->complete_func(op, success, att_ecode); + discovery_op_complete(op, success, att_ecode); } static void discover_primary_cb(bool success, uint8_t att_ecode, @@ -1007,6 +1093,10 @@ /* Skip if service already active */ if (!gatt_db_service_get_active(attr)) queue_push_tail(op->pending_svcs, attr); + + /* Update last handle */ + if (end > op->last) + op->last = end; } secondary: @@ -1034,19 +1124,29 @@ success = false; done: - op->success = success; - op->complete_func(op, success, att_ecode); + discovery_op_complete(op, success, att_ecode); } static void notify_client_ready(struct bt_gatt_client *client, bool success, uint8_t att_ecode) { + const struct queue_entry *entry; + if (!client->ready_callback || client->ready) return; bt_gatt_client_ref(client); client->ready = success; client->ready_callback(success, att_ecode, client->ready_data); + + /* Notify clones */ + for (entry = queue_get_entries(client->clones); entry; + entry = entry->next) { + struct bt_gatt_client *clone = entry->data; + + notify_client_ready(clone, success, att_ecode); + } + bt_gatt_client_unref(client); } @@ -1116,9 +1216,6 @@ { struct notify_data *notify_data = data; - /* Increment the per-characteristic ref count of notify handlers */ - __sync_fetch_and_add(¬ify_data->chrc->notify_count, 1); - notify_data->att_id = 0; notify_data->callback(0, notify_data->user_data); } @@ -1174,7 +1271,6 @@ struct notify_data *notify_data = user_data; uint16_t att_ecode; - assert(!notify_data->chrc->notify_count); assert(notify_data->chrc->ccc_write_id); notify_data->chrc->ccc_write_id = 0; @@ -1264,6 +1360,9 @@ notify_data->id = client->next_reg_id++; + /* Increment the per-characteristic ref count of notify handlers */ + __sync_fetch_and_add(¬ify_data->chrc->notify_count, 1); + /* * If a write to the CCC descriptor is in progress, then queue this * request. @@ -1274,9 +1373,9 @@ } /* - * If the ref count is not zero, then notifications are already enabled. + * If the ref count > 1, then notifications are already enabled. */ - if (chrc->notify_count > 0 || !chrc->ccc_handle) { + if (chrc->notify_count > 1 || !chrc->ccc_handle) { complete_notify_request(notify_data); return notify_data->id; } @@ -1361,6 +1460,7 @@ struct service_changed_op *next_sc_op; uint16_t start_handle = op->start; uint16_t end_handle = op->end; + const struct queue_entry *entry; client->in_svc_chngd = false; @@ -1377,6 +1477,16 @@ client->svc_chngd_callback(start_handle, end_handle, client->svc_chngd_data); + /* Notify clones */ + for (entry = queue_get_entries(client->clones); entry; + entry = entry->next) { + struct bt_gatt_client *clone = entry->data; + + if (clone->svc_chngd_callback) + clone->svc_chngd_callback(start_handle, end_handle, + clone->svc_chngd_data); + } + /* Process any queued events */ next_sc_op = queue_pop_head(client->svc_chngd_queue); if (next_sc_op) { @@ -1406,22 +1516,6 @@ { struct discovery_op *op; - /* On full database reset just re-run attribute discovery */ - if (start_handle == 0x0001 && end_handle == 0xffff) - goto discover; - - /* Invalidate and remove all effected notify callbacks */ - gatt_client_remove_all_notify_in_range(client, start_handle, - end_handle); - gatt_client_remove_notify_chrcs_in_range(client, start_handle, - end_handle); - - /* Remove all services that overlap the modified range since we'll - * rediscover them - */ - gatt_db_clear_range(client->db, start_handle, end_handle); - -discover: op = discovery_op_create(client, start_handle, end_handle, service_changed_complete, service_changed_failure); @@ -1528,9 +1622,25 @@ if (!op) return false; + /* + * BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 546: + * + * 4.3.1 Exchange MTU + * + * This sub-procedure shall not be used on a BR/EDR physical link since + * the MTU size is negotiated using L2CAP channel configuration + * procedures. + */ + if (bt_att_get_link_type(client->att) == BT_ATT_LINK_BREDR) + goto discover; + + /* Check if MTU needs to be send */ + mtu = MAX(BT_ATT_DEFAULT_LE_MTU, mtu); + if (mtu == BT_ATT_DEFAULT_LE_MTU) + goto discover; + /* Configure the MTU */ - client->mtu_req_id = bt_gatt_exchange_mtu(client->att, - MAX(BT_ATT_DEFAULT_LE_MTU, mtu), + client->mtu_req_id = bt_gatt_exchange_mtu(client->att, mtu, exchange_mtu_cb, discovery_op_ref(op), discovery_op_unref); @@ -1542,6 +1652,20 @@ client->in_init = true; return true; + +discover: + client->discovery_req = bt_gatt_discover_all_primary_services( + client->att, NULL, + discover_primary_cb, + discovery_op_ref(op), + discovery_op_unref); + if (!client->discovery_req) { + discovery_op_free(op); + return false; + } + + client->in_init = true; + return true; } struct pdu_data { @@ -1555,7 +1679,6 @@ struct notify_data *notify_data = user_data; struct notify_data *next_data; - assert(!notify_data->chrc->notify_count); assert(notify_data->chrc->ccc_write_id); notify_data->chrc->ccc_write_id = 0; @@ -1581,6 +1704,7 @@ */ if (notify_data->att_id) { bt_att_cancel(notify_data->client->att, notify_data->att_id); + notify_data->att_id = 0; goto done; } @@ -1632,7 +1756,7 @@ queue_foreach(client->notify_list, notify_handler, &pdu_data); - if (opcode == BT_ATT_OP_HANDLE_VAL_IND) + if (opcode == BT_ATT_OP_HANDLE_VAL_IND && !client->parent) bt_att_send(client->att, BT_ATT_OP_HANDLE_VAL_CONF, NULL, 0, NULL, NULL, NULL); @@ -1660,11 +1784,17 @@ gatt_db_unref(client->db); + queue_destroy(client->clones, NULL); queue_destroy(client->svc_chngd_queue, free); queue_destroy(client->long_write_queue, request_unref); queue_destroy(client->notify_chrcs, notify_chrc_free); queue_destroy(client->pending_requests, request_unref); + if (client->parent) { + queue_remove(client->parent->clones, client); + bt_gatt_client_unref(client->parent); + } + free(client); } @@ -1685,21 +1815,18 @@ notify_client_ready(client, false, 0); } -struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, - struct bt_att *att, - uint16_t mtu) +static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, + struct bt_att *att) { struct bt_gatt_client *client; - if (!att || !db) - return NULL; - client = new0(struct bt_gatt_client, 1); client->disc_id = bt_att_register_disconnect(att, att_disconnect_cb, client, NULL); if (!client->disc_id) goto fail; + client->clones = queue_new(); client->long_write_queue = queue_new(); client->svc_chngd_queue = queue_new(); client->notify_list = queue_new(); @@ -1719,14 +1846,56 @@ client->att = bt_att_ref(att); client->db = gatt_db_ref(db); - if (!gatt_client_init(client, mtu)) - goto fail; - - return bt_gatt_client_ref(client); + return client; fail: bt_gatt_client_free(client); return NULL; + +} + +struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, + struct bt_att *att, + uint16_t mtu) +{ + struct bt_gatt_client *client; + + if (!att || !db) + return NULL; + + client = gatt_client_new(db, att); + if (!client) + return NULL; + + if (!gatt_client_init(client, mtu)) { + bt_gatt_client_free(client); + return NULL; + } + + return bt_gatt_client_ref(client); +} + +struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client) +{ + struct bt_gatt_client *clone; + + if (!client) + return NULL; + + clone = gatt_client_new(client->db, client->att); + if (!clone) + return NULL; + + queue_push_tail(client->clones, clone); + + /* + * Reference the parent since the clones depend on it to propagate + * service changed and ready callbacks. + */ + clone->parent = bt_gatt_client_ref(client); + clone->ready = client->ready; + + return bt_gatt_client_ref(clone); } struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client) @@ -2156,7 +2325,9 @@ goto done; } - if (opcode != BT_ATT_OP_READ_BLOB_RSP || (!pdu && length)) { + if ((!op->offset && opcode != BT_ATT_OP_READ_RSP) + || (op->offset && opcode != BT_ATT_OP_READ_BLOB_RSP) + || (!pdu && length)) { success = false; goto done; } @@ -2209,7 +2380,9 @@ { struct request *req; struct read_long_op *op; + uint8_t att_op; uint8_t pdu[4]; + uint16_t pdu_len; if (!client) return 0; @@ -2233,12 +2406,32 @@ req->destroy = destroy_read_long_op; put_le16(value_handle, pdu); - put_le16(offset, pdu + 2); + pdu_len = sizeof(value_handle); + + /* + * Core v4.2, part F, section 1.3.4.4.5: + * If the attribute value has a fixed length that is less than or equal + * to (ATT_MTU - 3) octets in length, then an Error Response can be sent + * with the error code «Attribute Not Long». + * + * To remove need for caller to handle "Attribute Not Long" error when + * reading characteristics with short values, use Read Request for + * reading first part of characteristics value instead of Read Blob + * Request. Both are allowed in this case. + */ + + if (op->offset) { + att_op = BT_ATT_OP_READ_BLOB_REQ; + pdu_len += sizeof(op->offset); + + put_le16(op->offset, pdu + 2); + } else { + att_op = BT_ATT_OP_READ_REQ; + } + + req->att_id = bt_att_send(client->att, att_op, pdu, pdu_len, + read_long_cb, req, request_unref); - req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ, - pdu, sizeof(pdu), - read_long_cb, req, - request_unref); if (!req->att_id) { op->destroy = NULL; request_unref(req); @@ -2959,8 +3152,8 @@ if (!notify_data) return false; - assert(notify_data->chrc->notify_count > 0); - assert(!notify_data->chrc->ccc_write_id); + /* Remove data if it has been queued */ + queue_remove(notify_data->chrc->reg_notify_queue, notify_data); complete_unregister_notify(notify_data); return true; diff -Nru bluez-5.37/src/shared/gatt-client.h bluez-5.41/src/shared/gatt-client.h --- bluez-5.37/src/shared/gatt-client.h 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/src/shared/gatt-client.h 2016-03-18 02:18:03.000000000 +0000 @@ -32,6 +32,7 @@ struct bt_gatt_client *bt_gatt_client_new(struct gatt_db *db, struct bt_att *att, uint16_t mtu); +struct bt_gatt_client *bt_gatt_client_clone(struct bt_gatt_client *client); struct bt_gatt_client *bt_gatt_client_ref(struct bt_gatt_client *client); void bt_gatt_client_unref(struct bt_gatt_client *client); diff -Nru bluez-5.37/src/shared/gatt-db.c bluez-5.41/src/shared/gatt-db.c --- bluez-5.37/src/shared/gatt-db.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/gatt-db.c 2016-05-26 16:51:11.000000000 +0000 @@ -52,6 +52,8 @@ .value.u16 = GATT_CHARAC_UUID }; static const bt_uuid_t included_service_uuid = { .type = BT_UUID16, .value.u16 = GATT_INCLUDE_UUID }; +static const bt_uuid_t ext_desc_uuid = { .type = BT_UUID16, + .value.u16 = GATT_CHARAC_EXT_PROPER_UUID }; struct gatt_db { int ref_count; @@ -1456,10 +1458,68 @@ return le_to_uuid(decl->value, decl->value_len, uuid); } +static void read_ext_prop_value(struct gatt_db_attribute *attrib, + int err, const uint8_t *value, + size_t length, void *user_data) +{ + uint16_t *ext_prop = user_data; + + if (err || (length != sizeof(uint16_t))) + return; + + *ext_prop = (uint16_t) value[0]; +} + +static void read_ext_prop(struct gatt_db_attribute *attrib, + void *user_data) +{ + uint16_t *ext_prop = user_data; + + /* + * If ext_prop is set that means extended properties descriptor + * has been already found + */ + if (*ext_prop != 0) + return; + + if (bt_uuid_cmp(&ext_desc_uuid, &attrib->uuid)) + return; + + gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_REQ, NULL, + read_ext_prop_value, ext_prop); +} + +static uint8_t get_char_extended_prop(const struct gatt_db_attribute *attrib) +{ + uint16_t ext_prop; + + if (!attrib) + return 0; + + if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) + return 0; + + /* Check properties first */ + if (!(attrib->value[0] & BT_GATT_CHRC_PROP_EXT_PROP)) + return 0; + + ext_prop = 0; + + /* + * Cast needed for foreach function. We do not change attrib during + * this call + */ + gatt_db_service_foreach_desc((struct gatt_db_attribute *) attrib, + read_ext_prop, &ext_prop); + + return ext_prop; +} + bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *value_handle, uint8_t *properties, + uint16_t *ext_prop, bt_uuid_t *uuid) { if (!attrib) @@ -1484,6 +1544,9 @@ if (properties) *properties = attrib->value[0]; + if (ext_prop) + *ext_prop = get_char_extended_prop(attrib); + if (value_handle) *value_handle = get_le16(attrib->value + 1); diff -Nru bluez-5.37/src/shared/gatt-db.h bluez-5.41/src/shared/gatt-db.h --- bluez-5.37/src/shared/gatt-db.h 2015-03-31 13:41:57.000000000 +0000 +++ bluez-5.41/src/shared/gatt-db.h 2016-05-26 16:51:11.000000000 +0000 @@ -199,6 +199,7 @@ uint16_t *handle, uint16_t *value_handle, uint8_t *properties, + uint16_t *ext_prop, bt_uuid_t *uuid); bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib, diff -Nru bluez-5.37/src/shared/gatt-server.c bluez-5.41/src/shared/gatt-server.c --- bluez-5.37/src/shared/gatt-server.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/src/shared/gatt-server.c 2016-05-26 16:51:11.000000000 +0000 @@ -72,6 +72,8 @@ uint16_t handle; uint16_t offset; uint16_t length; + + bool reliable_supported; }; static void prep_write_data_destroy(void *user_data) @@ -396,6 +398,9 @@ return 0; security = bt_att_get_security(server->att); + if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS) + return BT_ATT_ERROR_AUTHENTICATION; + if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH) return BT_ATT_ERROR_AUTHENTICATION; @@ -1088,11 +1093,97 @@ bt_att_send_error_rsp(server->att, opcode, 0, ecode); } +static bool append_prep_data(struct prep_write_data *prep_data, uint16_t handle, + uint16_t length, uint8_t *value) +{ + uint8_t *val; + uint16_t len; + + if (!length) + return true; + + len = prep_data->length + length; + + val = realloc(prep_data->value, len); + if (!val) + return false; + + memcpy(val + prep_data->length, value, length); + + prep_data->value = val; + prep_data->length = len; + + return true; +} + +static bool is_reliable_write_supported(const struct bt_gatt_server *server, + uint16_t handle) +{ + struct gatt_db_attribute *attr; + uint16_t ext_prop; + + attr = gatt_db_get_attribute(server->db, handle); + if (!attr) + return false; + + if (!gatt_db_attribute_get_char_data(attr, NULL, NULL, NULL, &ext_prop, + NULL)) + return false; + + return (ext_prop & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE); +} + +static bool prep_data_new(struct bt_gatt_server *server, + uint16_t handle, uint16_t offset, + uint16_t length, uint8_t *value) +{ + struct prep_write_data *prep_data; + + prep_data = new0(struct prep_write_data, 1); + + if (!append_prep_data(prep_data, handle, length, value)) { + prep_write_data_destroy(prep_data); + return false; + } + + prep_data->server = server; + prep_data->handle = handle; + prep_data->offset = offset; + + /* + * Handle is the value handle. We need characteristic declaration + * handle which in BlueZ is handle_value -1 + */ + prep_data->reliable_supported = is_reliable_write_supported(server, + handle - 1); + + queue_push_tail(server->prep_queue, prep_data); + + return true; +} + +static bool store_prep_data(struct bt_gatt_server *server, + uint16_t handle, uint16_t offset, + uint16_t length, uint8_t *value) +{ + struct prep_write_data *prep_data = NULL; + + /* + * Now lets check if prep write is a continuation of long write + * If so do aggregation of data + */ + prep_data = queue_peek_tail(server->prep_queue); + if (prep_data && (prep_data->handle == handle) && + (offset == (prep_data->length + prep_data->offset))) + return append_prep_data(prep_data, handle, length, value); + + return prep_data_new(server, handle, offset, length, value); +} + static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; - struct prep_write_data *prep_data = NULL; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; @@ -1126,33 +1217,18 @@ if (ecode) goto error; - prep_data = new0(struct prep_write_data, 1); - prep_data->length = length - 4; - if (prep_data->length) { - prep_data->value = malloc(prep_data->length); - if (!prep_data->value) { - ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; - goto error; - } + if (!store_prep_data(server, handle, offset, length - 4, + &((uint8_t *) pdu)[4])) { + ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; + goto error; } - prep_data->server = server; - prep_data->handle = handle; - prep_data->offset = offset; - memcpy(prep_data->value, pdu + 4, prep_data->length); - - queue_push_tail(server->prep_queue, prep_data); - bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL, NULL, NULL); return; error: - if (prep_data) - prep_write_data_destroy(prep_data); - bt_att_send_error_rsp(server->att, opcode, handle, ecode); - } static void exec_next_prep_write(struct bt_gatt_server *server, @@ -1204,10 +1280,21 @@ err = BT_ATT_ERROR_UNLIKELY; error: + queue_remove_all(server->prep_queue, NULL, NULL, + prep_write_data_destroy); + bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); } +static bool find_no_reliable_characteristic(const void *data, + const void *match_data) +{ + const struct prep_write_data *prep_data = data; + + return !prep_data->reliable_supported; +} + static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { @@ -1215,6 +1302,7 @@ uint8_t flags; uint8_t ecode; bool write; + uint16_t ehandle = 0; if (length != 1) { ecode = BT_ATT_ERROR_INVALID_PDU; @@ -1243,12 +1331,27 @@ return; } + /* If there is more than one prep request, we are in reliable session */ + if (queue_length(server->prep_queue) > 1) { + struct prep_write_data *prep_data; + + prep_data = queue_find(server->prep_queue, + find_no_reliable_characteristic, NULL); + if (prep_data) { + ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED; + ehandle = prep_data->handle; + goto error; + } + } + exec_next_prep_write(server, 0, 0); return; error: - bt_att_send_error_rsp(server->att, opcode, 0, ecode); + queue_remove_all(server->prep_queue, NULL, NULL, + prep_write_data_destroy); + bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); } static void exchange_mtu_cb(uint8_t opcode, const void *pdu, diff -Nru bluez-5.37/src/shared/tty.h bluez-5.41/src/shared/tty.h --- bluez-5.37/src/shared/tty.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.41/src/shared/tty.h 2016-05-26 16:51:11.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2016 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +static inline unsigned int tty_get_speed(int speed) +{ + switch (speed) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; +#ifdef B2500000 + case 2500000: + return B2500000; +#endif +#ifdef B3000000 + case 3000000: + return B3000000; +#endif +#ifdef B3500000 + case 3500000: + return B3500000; +#endif +#ifdef B3710000 + case 3710000: + return B3710000; +#endif +#ifdef B4000000 + case 4000000: + return B4000000; +#endif + } + + return 0; +} diff -Nru bluez-5.37/test/example-gatt-client bluez-5.41/test/example-gatt-client --- bluez-5.37/test/example-gatt-client 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/test/example-gatt-client 2016-05-26 16:51:11.000000000 +0000 @@ -1,8 +1,10 @@ -#!/usr/bin/python +#!/usr/bin/env python3 -import argparse import dbus -import gobject +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject import sys from dbus.mainloop.glib import DBusGMainLoop @@ -111,7 +113,7 @@ def start_client(): # Read the Body Sensor Location value and print it asynchronously. - body_snsr_loc_chrc[0].ReadValue(reply_handler=body_sensor_val_cb, + body_snsr_loc_chrc[0].ReadValue({}, reply_handler=body_sensor_val_cb, error_handler=generic_error_cb, dbus_interface=GATT_CHRC_IFACE) @@ -149,7 +151,7 @@ return True -def process_hr_service(service_path): +def process_hr_service(service_path, chrc_paths): service = bus.get_object(BLUEZ_SERVICE_NAME, service_path) service_props = service.GetAll(GATT_SERVICE_IFACE, dbus_interface=DBUS_PROP_IFACE) @@ -157,11 +159,11 @@ uuid = service_props['UUID'] if uuid != HR_SVC_UUID: - print('Service is not a Heart Rate Service: ' + uuid) return False + print('Heart Rate Service found: ' + service_path) + # Process the characteristics. - chrc_paths = service_props['Characteristics'] for chrc_path in chrc_paths: process_chrc(chrc_path) @@ -181,33 +183,36 @@ def main(): - # Prase the service path from the arguments. - parser = argparse.ArgumentParser( - description='D-Bus Heart Rate Service client example') - parser.add_argument('service_path', metavar='', - type=dbus.ObjectPath, nargs=1, - help='GATT service object path') - args = parser.parse_args() - service_path = args.service_path[0] - # Set up the main loop. DBusGMainLoop(set_as_default=True) global bus bus = dbus.SystemBus() global mainloop - mainloop = gobject.MainLoop() + mainloop = GObject.MainLoop() om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), DBUS_OM_IFACE) om.connect_to_signal('InterfacesRemoved', interfaces_removed_cb) - try: - if not process_hr_service(service_path): - sys.exit(1) - except dbus.DBusException as e: - print e.message - sys.exit(1) + objects = om.GetManagedObjects() + chrcs = [] + + for path, interfaces in objects.items(): + if GATT_CHRC_IFACE not in interfaces.keys(): + continue + chrcs.append(path) + + for path, interfaces in objects.items(): + if GATT_SERVICE_IFACE not in interfaces.keys(): + continue - print 'Heart Rate Service ready' + chrc_paths = [d for d in chrcs if d.startswith(path + "/")] + + if process_hr_service(path, chrc_paths): + break + + if not hr_service: + print('No Heart Rate Service found') + sys.exit(1) start_client() diff -Nru bluez-5.37/test/example-gatt-server bluez-5.41/test/example-gatt-server --- bluez-5.37/test/example-gatt-server 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/test/example-gatt-server 2016-07-17 21:13:15.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 import dbus import dbus.exceptions @@ -6,7 +6,11 @@ import dbus.service import array -import gobject +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject +import sys from random import randint @@ -37,6 +41,38 @@ _dbus_error_name = 'org.bluez.Error.Failed' +class Application(dbus.service.Object): + def __init__(self, bus): + self.path = '/' + self.services = [] + dbus.service.Object.__init__(self, bus, self.path) + self.add_service(HeartRateService(bus, 0)) + self.add_service(BatteryService(bus, 1)) + self.add_service(TestService(bus, 2)) + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_service(self, service): + self.services.append(service) + + @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') + def GetManagedObjects(self): + response = {} + print('GetManagedObjects') + + for service in self.services: + response[service.get_path()] = service.get_properties() + chrcs = service.get_characteristics() + for chrc in chrcs: + response[chrc.get_path()] = chrc.get_properties() + descs = chrc.get_descriptors() + for desc in descs: + response[desc.get_path()] = desc.get_properties() + + return response + + class Service(dbus.service.Object): PATH_BASE = '/org/bluez/example/service' @@ -81,22 +117,7 @@ if interface != GATT_SERVICE_IFACE: raise InvalidArgsException() - return self.get_properties[GATT_SERVICE_IFACE] - - @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') - def GetManagedObjects(self): - response = {} - print('GetManagedObjects') - - response[self.get_path()] = self.get_properties() - chrcs = self.get_characteristics() - for chrc in chrcs: - response[chrc.get_path()] = chrc.get_properties() - descs = chrc.get_descriptors() - for desc in descs: - response[desc.get_path()] = desc.get_properties() - - return response + return self.get_properties()[GATT_SERVICE_IFACE] class Characteristic(dbus.service.Object): @@ -143,15 +164,17 @@ if interface != GATT_CHRC_IFACE: raise InvalidArgsException() - return self.get_properties[GATT_CHRC_IFACE] + return self.get_properties()[GATT_CHRC_IFACE] - @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay') - def ReadValue(self): + @dbus.service.method(GATT_CHRC_IFACE, + in_signature='a{sv}', + out_signature='ay') + def ReadValue(self, options): print('Default ReadValue called, returning error') raise NotSupportedException() - @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay') - def WriteValue(self, value): + @dbus.service.method(GATT_CHRC_IFACE, in_signature='aya{sv}') + def WriteValue(self, value, options): print('Default WriteValue called, returning error') raise NotSupportedException() @@ -199,15 +222,17 @@ if interface != GATT_DESC_IFACE: raise InvalidArgsException() - return self.get_properties[GATT_CHRC_IFACE] + return self.get_properties()[GATT_CHRC_IFACE] - @dbus.service.method(GATT_DESC_IFACE, out_signature='ay') - def ReadValue(self): + @dbus.service.method(GATT_DESC_IFACE, + in_signature='a{sv}', + out_signature='ay') + def ReadValue(self, options): print ('Default ReadValue called, returning error') raise NotSupportedException() - @dbus.service.method(GATT_DESC_IFACE, in_signature='ay') - def WriteValue(self, value): + @dbus.service.method(GATT_DESC_IFACE, in_signature='aya{sv}') + def WriteValue(self, value, options): print('Default WriteValue called, returning error') raise NotSupportedException() @@ -267,7 +292,7 @@ if not self.notifying: return - gobject.timeout_add(1000, self.hr_msrmt_cb) + GObject.timeout_add(1000, self.hr_msrmt_cb) def StartNotify(self): if self.notifying: @@ -296,7 +321,7 @@ ['read'], service) - def ReadValue(self): + def ReadValue(self, options): # Return 'Chest' as the sensor location. return [ 0x01 ] @@ -310,7 +335,7 @@ ['write'], service) - def WriteValue(self, value): + def WriteValue(self, value, options): print('Heart Rate Control Point WriteValue called') if len(value) != 1: @@ -354,7 +379,7 @@ service) self.notifying = False self.battery_lvl = 100 - gobject.timeout_add(5000, self.drain_battery) + GObject.timeout_add(5000, self.drain_battery) def notify_battery_level(self): if not self.notifying: @@ -372,7 +397,7 @@ self.notify_battery_level() return True - def ReadValue(self): + def ReadValue(self, options): print('Battery Level read: ' + repr(self.battery_lvl)) return [dbus.Byte(self.battery_lvl)] @@ -404,6 +429,7 @@ Service.__init__(self, bus, index, self.TEST_SVC_UUID, False) self.add_characteristic(TestCharacteristic(bus, 0, self)) self.add_characteristic(TestEncryptCharacteristic(bus, 1, self)) + self.add_characteristic(TestSecureCharacteristic(bus, 2, self)) class TestCharacteristic(Characteristic): """ @@ -424,11 +450,11 @@ self.add_descriptor( CharacteristicUserDescriptionDescriptor(bus, 1, self)) - def ReadValue(self): + def ReadValue(self, options): print('TestCharacteristic Read: ' + repr(self.value)) return self.value - def WriteValue(self, value): + def WriteValue(self, value, options): print('TestCharacteristic Write: ' + repr(value)) self.value = value @@ -447,7 +473,7 @@ ['read', 'write'], characteristic) - def ReadValue(self): + def ReadValue(self, options): return [ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t') ] @@ -462,7 +488,7 @@ def __init__(self, bus, index, characteristic): self.writable = 'writable-auxiliaries' in characteristic.flags - self.value = array.array('B', 'This is a characteristic for testing') + self.value = array.array('B', b'This is a characteristic for testing') self.value = self.value.tolist() Descriptor.__init__( self, bus, index, @@ -470,10 +496,10 @@ ['read', 'write'], characteristic) - def ReadValue(self): + def ReadValue(self, options): return self.value - def WriteValue(self, value): + def WriteValue(self, value, options): if not self.writable: raise NotPermittedException() self.value = value @@ -496,11 +522,11 @@ self.add_descriptor( CharacteristicUserDescriptionDescriptor(bus, 3, self)) - def ReadValue(self): + def ReadValue(self, options): print('TestCharacteristic Read: ' + repr(self.value)) return self.value - def WriteValue(self, value): + def WriteValue(self, value, options): print('TestCharacteristic Write: ' + repr(value)) self.value = value @@ -518,17 +544,64 @@ ['encrypt-read', 'encrypt-write'], characteristic) - def ReadValue(self): + def ReadValue(self, options): return [ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t') ] -def register_service_cb(): - print('GATT service registered') +class TestSecureCharacteristic(Characteristic): + """ + Dummy test characteristic requiring secure connection. -def register_service_error_cb(error): - print('Failed to register service: ' + str(error)) + """ + TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef5' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.TEST_CHRC_UUID, + ['secure-read', 'secure-write'], + service) + self.value = [] + self.add_descriptor(TestEncryptDescriptor(bus, 2, self)) + self.add_descriptor( + CharacteristicUserDescriptionDescriptor(bus, 3, self)) + + def ReadValue(self, options): + print('TestCharacteristic Read: ' + repr(self.value)) + return self.value + + def WriteValue(self, value, options): + print('TestCharacteristic Write: ' + repr(value)) + self.value = value + + +class TestSecureDescriptor(Descriptor): + """ + Dummy test descriptor requiring secure connection. Returns a static value. + + """ + TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef6' + + def __init__(self, bus, index, characteristic): + Descriptor.__init__( + self, bus, index, + self.TEST_DESC_UUID, + ['secure-read', 'secure-write'], + characteristic) + + def ReadValue(self, options): + return [ + dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t') + ] + +def register_app_cb(): + print('GATT application registered') + + +def register_app_error_cb(error): + print('Failed to register application: ' + str(error)) mainloop.quit() @@ -537,8 +610,8 @@ DBUS_OM_IFACE) objects = remote_om.GetManagedObjects() - for o, props in objects.iteritems(): - if props.has_key(GATT_MANAGER_IFACE): + for o, props in objects.items(): + if GATT_MANAGER_IFACE in props.keys(): return o return None @@ -559,21 +632,13 @@ bus.get_object(BLUEZ_SERVICE_NAME, adapter), GATT_MANAGER_IFACE) - hr_service = HeartRateService(bus, 0) - bat_service = BatteryService(bus, 1) - test_service = TestService(bus, 2) - - mainloop = gobject.MainLoop() - - service_manager.RegisterService(hr_service.get_path(), {}, - reply_handler=register_service_cb, - error_handler=register_service_error_cb) - service_manager.RegisterService(bat_service.get_path(), {}, - reply_handler=register_service_cb, - error_handler=register_service_error_cb) - service_manager.RegisterService(test_service.get_path(), {}, - reply_handler=register_service_cb, - error_handler=register_service_error_cb) + app = Application(bus) + + mainloop = GObject.MainLoop() + + service_manager.RegisterApplication(app.get_path(), {}, + reply_handler=register_app_cb, + error_handler=register_app_error_cb) mainloop.run() diff -Nru bluez-5.37/test/simple-endpoint bluez-5.41/test/simple-endpoint --- bluez-5.37/test/simple-endpoint 2014-01-21 00:12:58.000000000 +0000 +++ bluez-5.41/test/simple-endpoint 2016-07-17 21:13:15.000000000 +0000 @@ -66,9 +66,9 @@ mainloop.quit() @dbus.service.method("org.bluez.MediaEndpoint1", - in_signature="", out_signature="") - def ClearConfiguration(self): - print("ClearConfiguration") + in_signature="o", out_signature="") + def ClearConfiguration(self, transport): + print("ClearConfiguration (%s)" % (transport)) @dbus.service.method("org.bluez.MediaEndpoint1", in_signature="oay", out_signature="") diff -Nru bluez-5.37/test/test-discovery bluez-5.41/test/test-discovery --- bluez-5.37/test/test-discovery 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/test/test-discovery 2016-03-18 02:18:03.000000000 +0000 @@ -115,6 +115,18 @@ option_list = [ make_option("-i", "--device", action="store", type="string", dest="dev_id"), + make_option("-u", "--uuids", action="store", + type="string", dest="uuids", + help="Filtered service UUIDs [uuid1,uuid2,...]"), + make_option("-r", "--rssi", action="store", + type="int", dest="rssi", + help="RSSI threshold value"), + make_option("-p", "--pathloss", action="store", + type="int", dest="pathloss", + help="Pathloss threshold value"), + make_option("-t", "--transport", action="store", + type="string", dest="transport", + help="Type of scan to run (le/bredr/auto)"), make_option("-c", "--compact", action="store_true", dest="compact"), ] @@ -144,6 +156,26 @@ if "org.bluez.Device1" in interfaces: devices[path] = interfaces["org.bluez.Device1"] + scan_filter = dict() + + if options.uuids: + uuids = [] + uuid_list = options.uuids.split(',') + for uuid in uuid_list: + uuids.append(uuid) + + scan_filter.update({ "UUIDs": uuids }) + + if options.rssi: + scan_filter.update({ "RSSI": dbus.Int16(options.rssi) }) + + if options.pathloss: + scan_filter.update({ "Pathloss": dbus.UInt16(options.pathloss) }) + + if options.transport: + scan_filter.update({ "Transport": options.transport }) + + adapter.SetDiscoveryFilter(scan_filter) adapter.StartDiscovery() mainloop = GObject.MainLoop() diff -Nru bluez-5.37/test/test-gatt-profile bluez-5.41/test/test-gatt-profile --- bluez-5.37/test/test-gatt-profile 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/test/test-gatt-profile 2016-05-26 16:51:11.000000000 +0000 @@ -15,46 +15,116 @@ import gobject as GObject import bluezutils -class GattProfile(dbus.service.Object): - @dbus.service.method("org.bluez.GattProfile1", - in_signature="", out_signature="") - def Release(self): - print("Release") - mainloop.quit() +BLUEZ_SERVICE_NAME = 'org.bluez' +GATT_MANAGER_IFACE = 'org.bluez.GattManager1' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' -if __name__ == '__main__': - dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) +GATT_PROFILE_IFACE = 'org.bluez.GattProfile1' + + +class InvalidArgsException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs' + + +class Application(dbus.service.Object): + def __init__(self, bus): + self.path = '/' + self.profiles = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_profile(self, profile): + self.profiles.append(profile) + + @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') + def GetManagedObjects(self): + response = {} + print('GetManagedObjects') + + for profile in self.profiles: + response[profile.get_path()] = profile.get_properties() + + return response + + +class Profile(dbus.service.Object): + PATH_BASE = '/org/bluez/example/profile' - bus = dbus.SystemBus() + def __init__(self, bus, uuids): + self.path = self.PATH_BASE + self.bus = bus + self.uuids = uuids + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_PROFILE_IFACE: { + 'UUIDs': self.uuids, + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + @dbus.service.method(GATT_PROFILE_IFACE, + in_signature="", + out_signature="") + def Release(self): + print("Release") + mainloop.quit() + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_PROFILE_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_PROFILE_IFACE] + + +def register_app_cb(): + print('GATT application registered') + + +def register_app_error_cb(error): + print('Failed to register application: ' + str(error)) + mainloop.quit() + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) - path = bluezutils.find_adapter().object_path + bus = dbus.SystemBus() - manager = dbus.Interface(bus.get_object("org.bluez", path), - "org.bluez.GattManager1") + path = bluezutils.find_adapter().object_path - option_list = [ - make_option("-u", "--uuid", action="store", - type="string", dest="uuid", - default=None), - make_option("-p", "--path", action="store", - type="string", dest="path", - default="/foo/bar/profile"), - ] + manager = dbus.Interface(bus.get_object("org.bluez", path), + GATT_MANAGER_IFACE) - opts = dbus.Dictionary({ }, signature='sv') + option_list = [make_option("-u", "--uuid", action="store", + type="string", dest="uuid", + default=None), + ] - parser = OptionParser(option_list=option_list) + opts = dbus.Dictionary({}, signature='sv') - (options, args) = parser.parse_args() + parser = OptionParser(option_list=option_list) - profile = GattProfile(bus, options.path) + (options, args) = parser.parse_args() - mainloop = GObject.MainLoop() + mainloop = GObject.MainLoop() - if not options.uuid: - options.uuid = str(uuid.uuid4()) + if not options.uuid: + options.uuid = str(uuid.uuid4()) - uuids = { options.uuid } - manager.RegisterProfile(options.path, uuids, opts) + app = Application(bus) + profile = Profile(bus, [options.uuid]) + app.add_profile(profile) + manager.RegisterApplication(app.get_path(), {}, + reply_handler=register_app_cb, + error_handler=register_app_error_cb) - mainloop.run() + mainloop.run() diff -Nru bluez-5.37/TODO bluez-5.41/TODO --- bluez-5.37/TODO 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/TODO 2016-03-18 02:18:03.000000000 +0000 @@ -136,11 +136,6 @@ Priority: Medium Complexity: C1 -- Persist client attribute cache across reboots. - - Priority: Medium - Complexity: C4 - - Move all daemon plugins and profiles that are GATT based to use shared/gatt-client instead of attrib/*. This is a complicated task that potentially needs a new plugin/profile probing interface and a lot of @@ -162,12 +157,6 @@ Priority: Low Complexity: C1 -- Implement the server portion of doc/gatt-api.txt using shared/gatt-server once - it exists. - - Priority: Medium - Complexity: C4 - - Send out indications from the "Service Changed" characteristic upon reconnection if a bonded device is not connected when the local database is modified. @@ -195,94 +184,5 @@ Priority: Medium Complexity: C1 -- The recently added support for ATT signed writes requires the following kernel - modules to be enabled: - - CONFIG_CRYPTO_USER_API - CONFIG_CRYPTO_USER_API_HASH - CONFIG_CRYPTO_USER_API_SKCIPHER - - Currently, if these are not enabled, bt_att_new silently returns NULL. We - should handle this more gracefully by not supporting signed writes if we can't - initialize bt_crypto while succeeding bt_att initialization regardless. - - This behavior should be documented in the README. - - Priority: High - Complexity: C1 - - -ATT/GATT (old/outdated) -======================= - -- At the moment authentication and authorization is not supported at the - same time, read/write requirements in the attribute server needs to - be extended. According to Bluetooth Specification a server shall check - authentication and authorization requirements before any other check is - performed. - - Priority: Medium - Complexity: C1 - -- Implement ATT PDU validation. Malformed PDUs can cause division by zero - when decoding PDUs. A proper error PDU should be returned for this case. - See decoding function in att.c file. - - Priority: Medium - Complexity: C1 - -- Refactor read_by_group() and read_by_type() in src/attrib-server.c - (they've grown simply too big). First step could be to move out the - long for-loops to new functions called e.g. get_groups() and get_types(). - - Priority: Low - Complexity: C1 - -- Agent for characteristics: Agent interface should be extended to support - authorization per characteristic if the remote is not in the trusted list. - - Priority: Low - Complexity: C1 - -- gatttool should have the ability to wait for req responses before - quitting (some servers require a small sleep even with cmd's). Maybe a - --delay-exit or --timeout command line switch. - - Priority: Low - Complexity: C1 - -- Client needs to export a property in the Device Characteristic hierarchy - to manage characteristic value changes reports in the remote device. - Currently, Client Characteristic Configuration attribute is not exposed - as an object. The user needs to use gatttool to change the value of the - this attribute to receive notification/indications. Export this attribute - as a property is a proposal that needs further discussion. - - Priority: Low - Complexity: C1 - -- Attribute server should process queued GATT/ATT commands if the - client disconnects. The client can simply send a command and quit, - without wait for a response(ex: Write Command). For this scenario - that the client disconnects the link quickly the queued received - command is ignored. - - Priority: Low - Complecity: C1 - -- Implement Server characteristic Configuration support in the attribute - server to manage characteristic value broadcasting. There is a single - instance of the Server Characteristic Configuration for all clients. - See Volume 3, Part G, section 3.3.3.4 for more information. - - Priority: Low - Complexity: C1 - -- Long write is not implemented. Attribute server, client and command line - tool shall be changed to support this feature. - - Priority: Low - Complexity: C2 - Management Interface ==================== diff -Nru bluez-5.37/tools/bccmd.c bluez-5.41/tools/bccmd.c --- bluez-5.37/tools/bccmd.c 2015-07-07 17:21:11.000000000 +0000 +++ bluez-5.41/tools/bccmd.c 2016-05-26 16:51:11.000000000 +0000 @@ -36,6 +36,8 @@ #include "lib/hci.h" #include "lib/hci_lib.h" +#include "src/shared/tty.h" + #include "csr.h" #define CSR_TRANSPORT_UNKNOWN 0 @@ -1193,34 +1195,8 @@ device = strdup(optarg); break; case 'b': - switch (atoi(optarg)) { - case 9600: bcsp_rate = B9600; break; - case 19200: bcsp_rate = B19200; break; - case 38400: bcsp_rate = B38400; break; - case 57600: bcsp_rate = B57600; break; - case 115200: bcsp_rate = B115200; break; - case 230400: bcsp_rate = B230400; break; - case 460800: bcsp_rate = B460800; break; - case 500000: bcsp_rate = B500000; break; - case 576000: bcsp_rate = B576000; break; - case 921600: bcsp_rate = B921600; break; - case 1000000: bcsp_rate = B1000000; break; - case 1152000: bcsp_rate = B1152000; break; - case 1500000: bcsp_rate = B1500000; break; - case 2000000: bcsp_rate = B2000000; break; -#ifdef B2500000 - case 2500000: bcsp_rate = B2500000; break; -#endif -#ifdef B3000000 - case 3000000: bcsp_rate = B3000000; break; -#endif -#ifdef B3500000 - case 3500000: bcsp_rate = B3500000; break; -#endif -#ifdef B4000000 - case 4000000: bcsp_rate = B4000000; break; -#endif - default: + bcsp_rate = tty_get_speed(atoi(optarg)); + if (!bcsp_rate) { printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n"); bcsp_rate = B38400; } diff -Nru bluez-5.37/tools/bluemoon.c bluez-5.41/tools/bluemoon.c --- bluez-5.37/tools/bluemoon.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/tools/bluemoon.c 2016-05-26 16:51:11.000000000 +0000 @@ -620,6 +620,7 @@ { 0x09, "iBT 1.5 (AG610)" }, { 0x0a, "iBT 2.1 (AG620)" }, { 0x0b, "iBT 3.0 (LnP)" }, + { 0x0c, "iBT 3.0 (WsP)" }, { } }; diff -Nru bluez-5.37/tools/btattach.c bluez-5.41/tools/btattach.c --- bluez-5.37/tools/btattach.c 2015-09-28 12:16:53.000000000 +0000 +++ bluez-5.41/tools/btattach.c 2016-05-26 16:51:11.000000000 +0000 @@ -46,9 +46,10 @@ #include "src/shared/mainloop.h" #include "src/shared/timeout.h" #include "src/shared/util.h" +#include "src/shared/tty.h" #include "src/shared/hci.h" -static int open_serial(const char *path) +static int open_serial(const char *path, unsigned int speed) { struct termios ti; int fd, saved_ldisc, ldisc = N_HCI; @@ -75,7 +76,7 @@ memset(&ti, 0, sizeof(ti)); cfmakeraw(&ti); - ti.c_cflag |= (B115200 | CLOCAL | CREAD); + ti.c_cflag |= (speed | CLOCAL | CREAD); /* Set flow control */ ti.c_cflag |= CRTSCTS; @@ -106,11 +107,11 @@ } static int attach_proto(const char *path, unsigned int proto, - unsigned int flags) + unsigned int speed, unsigned int flags) { int fd, dev_id; - fd = open_serial(path); + fd = open_serial(path, speed); if (fd < 0) return -1; @@ -189,6 +190,7 @@ "\t-B, --bredr Attach BR/EDR controller\n" "\t-A, --amp Attach AMP controller\n" "\t-P, --protocol Specify protocol type\n" + "\t-S, --speed Specify which baudrate to use\n" "\t-h, --help Show help options\n"); } @@ -196,6 +198,7 @@ { "bredr", required_argument, NULL, 'B' }, { "amp", required_argument, NULL, 'A' }, { "protocol", required_argument, NULL, 'P' }, + { "speed", required_argument, NULL, 'S' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } @@ -223,11 +226,12 @@ bool raw_device = false; sigset_t mask; int exit_status, count = 0, proto_id = HCI_UART_H4; + unsigned int speed = B115200; for (;;) { int opt; - opt = getopt_long(argc, argv, "B:A:P:Rvh", + opt = getopt_long(argc, argv, "B:A:P:S:Rvh", main_options, NULL); if (opt < 0) break; @@ -242,6 +246,13 @@ case 'P': proto = optarg; break; + case 'S': + speed = tty_get_speed(atoi(optarg)); + if (!speed) { + fprintf(stderr, "Invalid speed: %s\n", optarg); + return EXIT_FAILURE; + } + break; case 'R': raw_device = true; break; @@ -296,7 +307,7 @@ if (raw_device) flags = (1 << HCI_UART_RAW_DEVICE); - fd = attach_proto(bredr_path, proto_id, flags); + fd = attach_proto(bredr_path, proto_id, speed, flags); if (fd >= 0) { mainloop_add_fd(fd, 0, uart_callback, NULL, NULL); count++; @@ -315,7 +326,7 @@ if (raw_device) flags = (1 << HCI_UART_RAW_DEVICE); - fd = attach_proto(amp_path, proto_id, flags); + fd = attach_proto(amp_path, proto_id, speed, flags); if (fd >= 0) { mainloop_add_fd(fd, 0, uart_callback, NULL, NULL); count++; diff -Nru bluez-5.37/tools/btgatt-client.c bluez-5.41/tools/btgatt-client.c --- bluez-5.37/tools/btgatt-client.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/tools/btgatt-client.c 2016-05-26 16:51:11.000000000 +0000 @@ -297,18 +297,20 @@ { uint16_t handle, value_handle; uint8_t properties; + uint16_t ext_prop; bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, &handle, &value_handle, &properties, + &ext_prop, &uuid)) return; printf("\t " COLOR_YELLOW "charac" COLOR_OFF - " - start: 0x%04x, value: 0x%04x, " - "props: 0x%02x, uuid: ", - handle, value_handle, properties); + " - start: 0x%04x, value: 0x%04x, " + "props: 0x%02x, ext_props: 0x%04x, uuid: ", + handle, value_handle, properties, ext_prop); print_uuid(&uuid); gatt_db_service_foreach_desc(attr, print_desc, NULL); @@ -658,7 +660,7 @@ static void cmd_write_value(struct client *cli, char *cmd_str) { - int opt, i; + int opt, i, val; char *argvbuf[516]; char **argv = argvbuf; int argc = 1; @@ -726,19 +728,14 @@ } for (i = 1; i < argc; i++) { - if (strlen(argv[i]) != 2) { - printf("Invalid value byte: %s\n", - argv[i]); - goto done; - } - - value[i-1] = strtol(argv[i], &endptr, 0); + val = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' - || errno == ERANGE) { + || errno == ERANGE || val < 0 || val > 255) { printf("Invalid value byte: %s\n", argv[i]); goto done; } + value[i-1] = val; } } @@ -793,7 +790,7 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str) { - int opt, i; + int opt, i, val; char *argvbuf[516]; char **argv = argvbuf; int argc = 1; @@ -865,21 +862,15 @@ } for (i = 2; i < argc; i++) { - if (strlen(argv[i]) != 2) { - printf("Invalid value byte: %s\n", - argv[i]); - free(value); - return; - } - - value[i-2] = strtol(argv[i], &endptr, 0); + val = strtol(argv[i], &endptr, 0); if (endptr == argv[i] || *endptr != '\0' - || errno == ERANGE) { + || errno == ERANGE || val < 0 || val > 255) { printf("Invalid value byte: %s\n", argv[i]); free(value); return; } + value[i-2] = val; } } @@ -909,7 +900,7 @@ static void cmd_write_prepare(struct client *cli, char *cmd_str) { - int opt, i; + int opt, i, val; char *argvbuf[516]; char **argv = argvbuf; int argc = 0; @@ -1002,18 +993,14 @@ } for (i = 2; i < argc; i++) { - if (strlen(argv[i]) != 2) { - printf("Invalid value byte: %s\n", argv[i]); - free(value); - return; - } - - value[i-2] = strtol(argv[i], &endptr, 0); - if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) { + val = strtol(argv[i], &endptr, 0); + if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE + || val < 0 || val > 255) { printf("Invalid value byte: %s\n", argv[i]); free(value); return; } + value[i-2] = val; } done: diff -Nru bluez-5.37/tools/btgatt-server.c bluez-5.41/tools/btgatt-server.c --- bluez-5.37/tools/btgatt-server.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/tools/btgatt-server.c 2016-05-26 16:51:11.000000000 +0000 @@ -419,7 +419,8 @@ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, - BT_GATT_CHRC_PROP_READ, + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_EXT_PROP, gap_device_name_read_cb, gap_device_name_write_cb, server); @@ -931,18 +932,20 @@ { uint16_t handle, value_handle; uint8_t properties; + uint16_t ext_prop; bt_uuid_t uuid; if (!gatt_db_attribute_get_char_data(attr, &handle, &value_handle, &properties, + &ext_prop, &uuid)) return; printf("\t " COLOR_YELLOW "charac" COLOR_OFF - " - start: 0x%04x, value: 0x%04x, " - "props: 0x%02x, uuid: ", - handle, value_handle, properties); + " - start: 0x%04x, value: 0x%04x, " + "props: 0x%02x, ext_prop: 0x%04x, uuid: ", + handle, value_handle, properties, ext_prop); print_uuid(&uuid); gatt_db_service_foreach_desc(attr, print_desc, NULL); diff -Nru bluez-5.37/tools/btinfo.c bluez-5.41/tools/btinfo.c --- bluez-5.37/tools/btinfo.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/tools/btinfo.c 2016-07-17 21:13:15.000000000 +0000 @@ -79,7 +79,7 @@ #define HCI_UP (1 << 0) -#define HCI_BREDR 0x00 +#define HCI_PRIMARY 0x00 #define HCI_AMP 0x01 static struct hci_dev_info hci_info; @@ -128,7 +128,7 @@ printf("HCI revision: %u\n", le16_to_cpu(rsp->hci_rev)); switch (hci_type) { - case HCI_BREDR: + case HCI_PRIMARY: printf("LMP version: %u\n", rsp->lmp_ver); printf("LMP subversion: %u\n", le16_to_cpu(rsp->lmp_subver)); break; diff -Nru bluez-5.37/tools/btmgmt.c bluez-5.41/tools/btmgmt.c --- bluez-5.37/tools/btmgmt.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/tools/btmgmt.c 2016-03-18 02:18:03.000000000 +0000 @@ -2254,19 +2254,21 @@ { "help", 0, 0, 'h' }, { "le-only", 1, 0, 'l' }, { "bredr-only", 1, 0, 'b' }, + { "limited", 1, 0, 'L' }, { 0, 0, 0, 0 } }; static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv) { struct mgmt_cp_start_discovery cp; + uint8_t op = MGMT_OP_START_DISCOVERY; uint8_t type = SCAN_TYPE_DUAL; int opt; if (index == MGMT_INDEX_NONE) index = 0; - while ((opt = getopt_long(argc, argv, "+lbh", find_options, + while ((opt = getopt_long(argc, argv, "+lbLh", find_options, NULL)) != -1) { switch (opt) { case 'l': @@ -2277,6 +2279,9 @@ type |= SCAN_TYPE_BREDR; type &= ~SCAN_TYPE_LE; break; + case 'L': + op = MGMT_OP_START_LIMITED_DISCOVERY; + break; case 'h': find_usage(); optind = 0; @@ -2295,8 +2300,8 @@ memset(&cp, 0, sizeof(cp)); cp.type = type; - if (mgmt_send(mgmt, MGMT_OP_START_DISCOVERY, index, sizeof(cp), &cp, - find_rsp, NULL, NULL) == 0) { + if (mgmt_send(mgmt, op, index, sizeof(cp), &cp, find_rsp, + NULL, NULL) == 0) { error("Unable to send start_discovery cmd"); return noninteractive_quit(EXIT_FAILURE); } @@ -2587,7 +2592,7 @@ str2ba(argv[0], &cp.bdaddr); cp.type = type; - if (mgmt_send(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp, + if (mgmt_reply(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp, cancel_pair_rsp, NULL, NULL) == 0) { error("Unable to send cancel_pair_device cmd"); return noninteractive_quit(EXIT_FAILURE); diff -Nru bluez-5.37/tools/btproxy.c bluez-5.41/tools/btproxy.c --- bluez-5.37/tools/btproxy.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/tools/btproxy.c 2016-07-17 21:13:15.000000000 +0000 @@ -49,7 +49,7 @@ #include "src/shared/ecc.h" #include "monitor/bt.h" -#define HCI_BREDR 0x00 +#define HCI_PRIMARY 0x00 #define HCI_AMP 0x01 #define BTPROTO_HCI 1 @@ -758,7 +758,7 @@ const char *unix_path = NULL; unsigned short tcp_port = 0xb1ee; /* 45550 */ bool use_redirect = false; - uint8_t type = HCI_BREDR; + uint8_t type = HCI_PRIMARY; const char *str; sigset_t mask; diff -Nru bluez-5.37/tools/cltest.c bluez-5.41/tools/cltest.c --- bluez-5.37/tools/cltest.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/tools/cltest.c 2016-07-17 21:13:15.000000000 +0000 @@ -228,7 +228,7 @@ if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) continue; - if (((di.type & 0x30) >> 4) != HCI_BREDR) + if (((di.type & 0x30) >> 4) != HCI_PRIMARY) continue; if (!bacmp(&bdaddr_src, BDADDR_ANY)) { diff -Nru bluez-5.37/tools/eddystone.c bluez-5.41/tools/eddystone.c --- bluez-5.37/tools/eddystone.c 2015-07-30 19:17:18.000000000 +0000 +++ bluez-5.41/tools/eddystone.c 2016-03-18 02:18:03.000000000 +0000 @@ -149,7 +149,6 @@ cmd.data[0] = 0x02; /* Field length */ cmd.data[1] = 0x01; /* Flags */ - cmd.data[2] = 0x02; /* LE General Discoverable Mode */ cmd.data[2] |= 0x04; /* BR/EDR Not Supported */ cmd.data[3] = 0x03; /* Field length */ diff -Nru bluez-5.37/tools/gatt-service.c bluez-5.41/tools/gatt-service.c --- bluez-5.37/tools/gatt-service.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/tools/gatt-service.c 2016-07-17 21:13:15.000000000 +0000 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,7 @@ static DBusConnection *connection; struct characteristic { + char *service; char *uuid; char *path; uint8_t *value; @@ -63,10 +65,12 @@ }; struct descriptor { + struct characteristic *chr; char *uuid; char *path; uint8_t *value; int vlen; + const char **props; }; /* @@ -75,6 +79,7 @@ * property of the GattCharacteristic1. */ static const char *ias_alert_level_props[] = { "write-without-response", NULL }; +static const char *desc_props[] = { "read", "write", NULL }; static gboolean desc_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) @@ -86,13 +91,20 @@ return TRUE; } -static gboolean desc_get_value(const GDBusPropertyTable *property, +static gboolean desc_get_characteristic(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct descriptor *desc = user_data; - DBusMessageIter array; - printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &desc->chr->path); + + return TRUE; +} + +static bool desc_read(struct descriptor *desc, DBusMessageIter *iter) +{ + DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); @@ -103,7 +115,40 @@ dbus_message_iter_close_container(iter, &array); - return TRUE; + return true; +} + +static gboolean desc_get_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct descriptor *desc = user_data; + + printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid); + + return desc_read(desc, iter); +} + +static void desc_write(struct descriptor *desc, const uint8_t *value, int len) +{ + g_free(desc->value); + desc->value = g_memdup(value, len); + desc->vlen = len; + + g_dbus_emit_property_changed(connection, desc->path, + GATT_DESCRIPTOR_IFACE, "Value"); +} + +static int parse_value(DBusMessageIter *iter, const uint8_t **value, int *len) +{ + DBusMessageIter array; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, value, len); + + return 0; } static void desc_set_value(const GDBusPropertyTable *property, @@ -111,29 +156,48 @@ GDBusPendingPropertySet id, void *user_data) { struct descriptor *desc = user_data; - DBusMessageIter array; const uint8_t *value; - int vlen; + int len; printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid); - dbus_message_iter_recurse(iter, &array); - dbus_message_iter_get_fixed_array(&array, &value, &vlen); + if (parse_value(iter, &value, &len)) { + printf("Invalid value for Set('Value'...)\n"); + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } - g_free(desc->value); - desc->value = g_memdup(value, vlen); - desc->vlen = vlen; + desc_write(desc, value, len); g_dbus_pending_property_success(id); +} - g_dbus_emit_property_changed(connection, desc->path, - GATT_DESCRIPTOR_IFACE, "Value"); +static gboolean desc_get_props(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct descriptor *desc = data; + DBusMessageIter array; + int i; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + for (i = 0; desc->props[i]; i++) + dbus_message_iter_append_basic(&array, + DBUS_TYPE_STRING, &desc->props[i]); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; } static const GDBusPropertyTable desc_properties[] = { - { "UUID", "s", desc_get_uuid }, - { "Value", "ay", desc_get_value, desc_set_value, NULL }, + { "UUID", "s", desc_get_uuid }, + { "Characteristic", "o", desc_get_characteristic }, + { "Value", "ay", desc_get_value, desc_set_value, NULL }, + { "Flags", "as", desc_get_props, NULL, NULL }, { } }; @@ -147,13 +211,20 @@ return TRUE; } -static gboolean chr_get_value(const GDBusPropertyTable *property, +static gboolean chr_get_service(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { struct characteristic *chr = user_data; - DBusMessageIter array; - printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, + &chr->service); + + return TRUE; +} + +static bool chr_read(struct characteristic *chr, DBusMessageIter *iter) +{ + DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &array); @@ -163,7 +234,17 @@ dbus_message_iter_close_container(iter, &array); - return TRUE; + return true; +} + +static gboolean chr_get_value(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct characteristic *chr = user_data; + + printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid); + + return chr_read(chr, iter); } static gboolean chr_get_props(const GDBusPropertyTable *property, @@ -185,18 +266,27 @@ return TRUE; } +static void chr_write(struct characteristic *chr, const uint8_t *value, int len) +{ + g_free(chr->value); + chr->value = g_memdup(value, len); + chr->vlen = len; + + g_dbus_emit_property_changed(connection, chr->path, GATT_CHR_IFACE, + "Value"); +} + static void chr_set_value(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *user_data) { struct characteristic *chr = user_data; - DBusMessageIter array; - uint8_t *value; + const uint8_t *value; int len; printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid); - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + if (!parse_value(iter, &value, &len)) { printf("Invalid value for Set('Value'...)\n"); g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", @@ -204,25 +294,31 @@ return; } - dbus_message_iter_recurse(iter, &array); - dbus_message_iter_get_fixed_array(&array, &value, &len); - - g_free(chr->value); - chr->value = g_memdup(value, len); - chr->vlen = len; + chr_write(chr, value, len); g_dbus_pending_property_success(id); - g_dbus_emit_property_changed(connection, chr->path, - GATT_CHR_IFACE, "Value"); } static const GDBusPropertyTable chr_properties[] = { - { "UUID", "s", chr_get_uuid }, - { "Value", "ay", chr_get_value, chr_set_value, NULL }, - { "Flags", "as", chr_get_props, NULL, NULL }, + { "UUID", "s", chr_get_uuid }, + { "Service", "o", chr_get_service }, + { "Value", "ay", chr_get_value, chr_set_value, NULL }, + { "Flags", "as", chr_get_props, NULL, NULL }, { } }; +static gboolean service_get_primary(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + dbus_bool_t primary = TRUE; + + printf("Get Primary: %s\n", primary ? "True" : "False"); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary); + + return TRUE; +} + static gboolean service_get_uuid(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { @@ -256,6 +352,7 @@ } static const GDBusPropertyTable service_properties[] = { + { "Primary", "b", service_get_primary }, { "UUID", "s", service_get_uuid }, { "Includes", "ao", service_get_includes, NULL, service_exist_includes }, @@ -267,6 +364,7 @@ struct characteristic *chr = user_data; g_free(chr->uuid); + g_free(chr->service); g_free(chr->value); g_free(chr->path); g_free(chr); @@ -282,10 +380,187 @@ g_free(desc); } +static int parse_options(DBusMessageIter *iter, const char **device) +{ + DBusMessageIter dict; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + if (strcasecmp(key, "device") == 0) { + if (var != DBUS_TYPE_OBJECT_PATH) + return -EINVAL; + dbus_message_iter_get_basic(&value, device); + printf("Device: %s\n", *device); + } + + dbus_message_iter_next(&dict); + } + + return 0; +} + +static DBusMessage *chr_read_value(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct characteristic *chr = user_data; + DBusMessage *reply; + DBusMessageIter iter; + const char *device; + + if (!dbus_message_iter_init(msg, &iter)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + if (parse_options(&iter, &device)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY, + "No Memory"); + + dbus_message_iter_init_append(reply, &iter); + + chr_read(chr, &iter); + + return reply; +} + +static DBusMessage *chr_write_value(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct characteristic *chr = user_data; + DBusMessageIter iter; + const uint8_t *value; + int len; + const char *device; + + dbus_message_iter_init(msg, &iter); + + if (parse_value(&iter, &value, &len)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + if (parse_options(&iter, &device)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + chr_write(chr, value, len); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *chr_start_notify(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED, + "Not Supported"); +} + +static DBusMessage *chr_stop_notify(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + return g_dbus_create_error(msg, DBUS_ERROR_NOT_SUPPORTED, + "Not Supported"); +} + +static const GDBusMethodTable chr_methods[] = { + { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + chr_read_value) }, + { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, chr_write_value) }, + { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) }, + { GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) }, + { } +}; + +static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct descriptor *desc = user_data; + DBusMessage *reply; + DBusMessageIter iter; + const char *device; + + if (!dbus_message_iter_init(msg, &iter)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + if (parse_options(&iter, &device)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return g_dbus_create_error(msg, DBUS_ERROR_NO_MEMORY, + "No Memory"); + + dbus_message_iter_init_append(reply, &iter); + + desc_read(desc, &iter); + + return reply; +} + +static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct descriptor *desc = user_data; + DBusMessageIter iter; + const char *device; + const uint8_t *value; + int len; + + if (!dbus_message_iter_init(msg, &iter)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + if (parse_value(&iter, &value, &len)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + if (parse_options(&iter, &device)) + return g_dbus_create_error(msg, DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"); + + desc_write(desc, value, len); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable desc_methods[] = { + { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }), + GDBUS_ARGS({ "value", "ay" }), + desc_read_value) }, + { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }, + { "options", "a{sv}" }), + NULL, desc_write_value) }, + { } +}; + static gboolean register_characteristic(const char *chr_uuid, const uint8_t *value, int vlen, const char **props, const char *desc_uuid, + const char **desc_props, const char *service_path) { struct characteristic *chr; @@ -297,10 +572,11 @@ chr->value = g_memdup(value, vlen); chr->vlen = vlen; chr->props = props; + chr->service = g_strdup(service_path); chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++); if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE, - NULL, NULL, chr_properties, + chr_methods, NULL, chr_properties, chr, chr_iface_destroy)) { printf("Couldn't register characteristic interface\n"); chr_iface_destroy(chr); @@ -312,11 +588,13 @@ desc = g_new0(struct descriptor, 1); desc->uuid = g_strdup(desc_uuid); + desc->chr = chr; + desc->props = desc_props; desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++); if (!g_dbus_register_interface(connection, desc->path, GATT_DESCRIPTOR_IFACE, - NULL, NULL, desc_properties, + desc_methods, NULL, desc_properties, desc, desc_iface_destroy)) { printf("Couldn't register descriptor interface\n"); g_dbus_unregister_interface(connection, chr->path, @@ -360,6 +638,7 @@ &level, sizeof(level), ias_alert_level_props, READ_WRITE_DESCRIPTOR_UUID, + desc_props, service_path)) { printf("Couldn't register Alert Level characteristic (IAS)\n"); g_dbus_unregister_interface(connection, service_path, @@ -372,63 +651,55 @@ printf("Registered service: %s\n", service_path); } -static void register_external_service_reply(DBusPendingCall *call, - void *user_data) +static void register_app_reply(DBusMessage *reply, void *user_data) { - DBusMessage *reply = dbus_pending_call_steal_reply(call); DBusError derr; dbus_error_init(&derr); dbus_set_error_from_message(&derr, reply); if (dbus_error_is_set(&derr)) - printf("RegisterService: %s\n", derr.message); + printf("RegisterApplication: %s\n", derr.message); else - printf("RegisterService: OK\n"); + printf("RegisterApplication: OK\n"); - dbus_message_unref(reply); dbus_error_free(&derr); } -static void register_external_service(gpointer a, gpointer b) +static void register_app_setup(DBusMessageIter *iter, void *user_data) { - DBusConnection *conn = b; - const char *path = a; - DBusMessage *msg; - DBusPendingCall *call; - DBusMessageIter iter, dict; - - msg = dbus_message_new_method_call("org.bluez", "/org/bluez", - GATT_MGR_IFACE, "RegisterService"); - if (!msg) { - printf("Couldn't allocate D-Bus message\n"); - return; - } - - dbus_message_iter_init_append(msg, &iter); + const char *path = "/"; + DBusMessageIter dict; - dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict); + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict); /* TODO: Add options dictionary */ - dbus_message_iter_close_container(&iter, &dict); + dbus_message_iter_close_container(iter, &dict); +} - if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) { - dbus_message_unref(msg); +static void register_app(GDBusProxy *proxy) +{ + if (!g_dbus_proxy_method_call(proxy, "RegisterApplication", + register_app_setup, register_app_reply, + NULL, NULL)) { + printf("Unable to call RegisterApplication\n"); return; } - - dbus_pending_call_set_notify(call, register_external_service_reply, - NULL, NULL); - - dbus_pending_call_unref(call); } -static void connect_handler(DBusConnection *conn, void *user_data) +static void proxy_added_cb(GDBusProxy *proxy, void *user_data) { - g_slist_foreach(services, register_external_service, conn); + const char *iface; + + iface = g_dbus_proxy_get_interface(proxy); + + if (g_strcmp0(iface, GATT_MGR_IFACE)) + return; + + register_app(proxy); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, @@ -520,9 +791,10 @@ create_services(); - client = g_dbus_client_new(connection, "org.bluez", "/org/bluez"); + client = g_dbus_client_new(connection, "org.bluez", "/"); - g_dbus_client_set_connect_watch(client, connect_handler, NULL); + g_dbus_client_set_proxy_handlers(client, proxy_added_cb, NULL, NULL, + NULL); g_main_loop_run(main_loop); diff -Nru bluez-5.37/tools/hciattach.c bluez-5.41/tools/hciattach.c --- bluez-5.37/tools/hciattach.c 2015-09-04 01:19:36.000000000 +0000 +++ bluez-5.41/tools/hciattach.c 2016-05-26 16:51:11.000000000 +0000 @@ -46,6 +46,8 @@ #include "lib/hci.h" #include "lib/hci_lib.h" +#include "src/shared/tty.h" + #include "hciattach.h" struct uart_t { @@ -84,68 +86,12 @@ exit(1); } -int uart_speed(int s) -{ - switch (s) { - case 9600: - return B9600; - case 19200: - return B19200; - case 38400: - return B38400; - case 57600: - return B57600; - case 115200: - return B115200; - case 230400: - return B230400; - case 460800: - return B460800; - case 500000: - return B500000; - case 576000: - return B576000; - case 921600: - return B921600; - case 1000000: - return B1000000; - case 1152000: - return B1152000; - case 1500000: - return B1500000; - case 2000000: - return B2000000; -#ifdef B2500000 - case 2500000: - return B2500000; -#endif -#ifdef B3000000 - case 3000000: - return B3000000; -#endif -#ifdef B3500000 - case 3500000: - return B3500000; -#endif -#ifdef B3710000 - case 3710000: - return B3710000; -#endif -#ifdef B4000000 - case 4000000: - return B4000000; -#endif - default: - return B57600; - } -} - int set_speed(int fd, struct termios *ti, int speed) { - if (cfsetospeed(ti, uart_speed(speed)) < 0) + if (cfsetospeed(ti, tty_get_speed(speed)) < 0) return -errno; - if (cfsetispeed(ti, uart_speed(speed)) < 0) + if (cfsetispeed(ti, tty_get_speed(speed)) < 0) return -errno; if (tcsetattr(fd, TCSANOW, ti) < 0) @@ -646,7 +592,7 @@ fprintf(stderr, "Speed %d too high. Remaining at %d baud\n", u->speed, u->init_speed); u->speed = u->init_speed; - } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) { + } else if (!tty_get_speed(u->speed)) { /* Unknown speed. Why oh why can't we just pass an int to the kernel? */ fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n", u->speed, u->init_speed); diff -Nru bluez-5.37/tools/hciconfig.c bluez-5.41/tools/hciconfig.c --- bluez-5.37/tools/hciconfig.c 2015-10-30 03:30:13.000000000 +0000 +++ bluez-5.41/tools/hciconfig.c 2016-07-17 21:13:15.000000000 +0000 @@ -1151,7 +1151,7 @@ } hciver = hci_vertostr(ver.hci_ver); - if (((di.type & 0x30) >> 4) == HCI_BREDR) + if (((di.type & 0x30) >> 4) == HCI_PRIMARY) lmpver = lmp_vertostr(ver.lmp_ver); else lmpver = pal_vertostr(ver.lmp_ver); @@ -1161,7 +1161,7 @@ "\t%s Version: %s (0x%x) Subversion: 0x%x\n" "\tManufacturer: %s (%d)\n", hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev, - (((di.type & 0x30) >> 4) == HCI_BREDR) ? "LMP" : "PAL", + (((di.type & 0x30) >> 4) == HCI_PRIMARY) ? "LMP" : "PAL", lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver, bt_compidtostr(ver.manufacturer), ver.manufacturer); @@ -1902,7 +1902,7 @@ if (all && !hci_test_bit(HCI_RAW, &di->flags)) { print_dev_features(di, 0); - if (((di->type & 0x30) >> 4) == HCI_BREDR) { + if (((di->type & 0x30) >> 4) == HCI_PRIMARY) { print_pkt_type(di); print_link_policy(di); print_link_mode(di); diff -Nru bluez-5.37/tools/ibeacon.c bluez-5.41/tools/ibeacon.c --- bluez-5.37/tools/ibeacon.c 2015-03-11 10:01:57.000000000 +0000 +++ bluez-5.41/tools/ibeacon.c 2016-03-18 02:18:03.000000000 +0000 @@ -149,7 +149,6 @@ cmd.data[0] = 0x02; /* Field length */ cmd.data[1] = 0x01; /* Flags */ - cmd.data[2] = 0x02; /* LE General Discoverable Mode */ cmd.data[2] |= 0x04; /* BR/EDR Not Supported */ cmd.data[3] = 0x1a; /* Field length */ diff -Nru bluez-5.37/tools/mgmt-tester.c bluez-5.41/tools/mgmt-tester.c --- bluez-5.37/tools/mgmt-tester.c 2015-12-28 02:13:20.000000000 +0000 +++ bluez-5.41/tools/mgmt-tester.c 2016-07-17 21:13:16.000000000 +0000 @@ -428,6 +428,7 @@ bool just_works; bool client_enable_le; bool client_enable_sc; + bool client_enable_adv; bool expect_sc_key; bool force_power_off; bool addr_type_avail; @@ -3090,6 +3091,96 @@ .verify_alt_ev_func = verify_ltk, }; +static bool lk_is_authenticated(const struct mgmt_link_key_info *lk) +{ + switch (lk->type) { + case 0x00: /* Combination Key */ + case 0x01: /* Local Unit Key */ + case 0x02: /* Remote Unit Key */ + case 0x03: /* Debug Combination Key */ + if (lk->pin_len == 16) + return true; + return false; + case 0x05: /* Authenticated Combination Key generated from P-192 */ + case 0x08: /* Authenticated Combination Key generated from P-256 */ + return true; + default: + return false; + } +} + +static bool lk_is_sc(const struct mgmt_link_key_info *lk) +{ + switch (lk->type) { + case 0x07: /* Unauthenticated Combination Key generated from P-256 */ + case 0x08: /* Authenticated Combination Key generated from P-256 */ + return true; + default: + return false; + } +} + +static bool verify_link_key(const void *param, uint16_t length) +{ + struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; + const struct mgmt_ev_new_link_key *ev = param; + + if (length != sizeof(struct mgmt_ev_new_link_key)) { + tester_warn("Invalid new Link Key length %u != %zu", length, + sizeof(struct mgmt_ev_new_link_key)); + return false; + } + + if (test->just_works && lk_is_authenticated(&ev->key)) { + tester_warn("Authenticated key for just-works"); + return false; + } + + if (!test->just_works && !lk_is_authenticated(&ev->key)) { + tester_warn("Unauthenticated key for MITM"); + return false; + } + + if (test->expect_sc_key && !lk_is_sc(&ev->key)) { + tester_warn("Non-LE SC key for SC pairing"); + return false; + } + + if (!test->expect_sc_key && lk_is_sc(&ev->key)) { + tester_warn("SC key for Non-SC pairing"); + return false; + } + + return true; +} + +static uint16_t settings_powered_le_sc_bondable[] = { + MGMT_OP_SET_LE, + MGMT_OP_SET_SSP, + MGMT_OP_SET_BONDABLE, + MGMT_OP_SET_SECURE_CONN, + MGMT_OP_SET_POWERED, 0 }; + +static const struct generic_data pair_device_le_sc_success_test_3 = { + .setup_settings = settings_powered_le_sc_bondable, + .send_opcode = MGMT_OP_PAIR_DEVICE, + .send_func = pair_device_send_param_func, + .addr_type_avail = true, + .addr_type = 0x01, + .client_enable_sc = true, + .client_enable_ssp = true, + .client_enable_adv = true, + .expect_sc_key = true, + .io_cap = 0x02, /* KeyboardOnly */ + .client_io_cap = 0x02, /* KeyboardOnly */ + .expect_status = MGMT_STATUS_SUCCESS, + .expect_func = pair_device_expect_param_func, + .expect_alt_ev = MGMT_EV_NEW_LINK_KEY, + .expect_alt_ev_len = 26, + .verify_alt_ev_func = verify_link_key, +}; + static uint16_t settings_powered_connectable_bondable[] = { MGMT_OP_SET_BONDABLE, MGMT_OP_SET_CONNECTABLE, @@ -4808,11 +4899,12 @@ static void setup_bthost(void) { struct test_data *data = tester_get_data(); + const struct generic_data *test = data->test_data; struct bthost *bthost; bthost = hciemu_client_get_host(data->hciemu); bthost_set_cmd_complete_cb(bthost, client_cmd_complete, data); - if (data->hciemu_type == HCIEMU_TYPE_LE) + if (data->hciemu_type == HCIEMU_TYPE_LE || test->client_enable_adv) bthost_set_adv_enable(bthost, 0x01); else bthost_write_scan_enable(bthost, 0x03); @@ -6653,6 +6745,9 @@ test_le("Pair Device - LE SC Success 2", &pair_device_le_sc_success_test_2, NULL, test_command_generic); + test_bredrle("Pair Device - LE SC Success 3", + &pair_device_le_sc_success_test_3, + NULL, test_command_generic); test_bredrle("Pairing Acceptor - Legacy 1", &pairing_acceptor_legacy_1, NULL, diff -Nru bluez-5.37/tools/test-runner.c bluez-5.41/tools/test-runner.c --- bluez-5.37/tools/test-runner.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/tools/test-runner.c 2016-05-26 16:51:11.000000000 +0000 @@ -5,18 +5,18 @@ * Copyright (C) 2012-2014 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 library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser 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 + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ diff -Nru bluez-5.37/unit/test-crypto.c bluez-5.41/unit/test-crypto.c --- bluez-5.37/unit/test-crypto.c 2015-06-15 09:33:25.000000000 +0000 +++ bluez-5.41/unit/test-crypto.c 2016-07-17 21:13:16.000000000 +0000 @@ -34,10 +34,54 @@ static struct bt_crypto *crypto; +static void print_debug(const char *str, void *user_data) +{ + tester_debug("%s", str); +} + +static void test_h6(gconstpointer data) +{ + const uint8_t w[16] = { + 0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34, + 0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec }; + const uint8_t m[4] = { 0x72, 0x62, 0x65, 0x6c }; + const uint8_t exp[16] = { + 0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8, + 0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d }; + uint8_t res[16]; + + tester_debug("W:"); + util_hexdump(' ', w, 16, print_debug, NULL); + + tester_debug("M:"); + util_hexdump(' ', m, 4, print_debug, NULL); + + if (!bt_crypto_h6(crypto, w, m, res)) { + tester_test_failed(); + return; + } + + tester_debug("Expected:"); + util_hexdump(' ', exp, 16, print_debug, NULL); + + tester_debug("Result:"); + util_hexdump(' ', res, 16, print_debug, NULL); + + + if (memcmp(res, exp, 16)) { + tester_test_failed(); + return; + } + + tester_test_passed(); +} + struct test_data { const uint8_t *msg; uint16_t msg_len; const uint8_t *t; + const uint8_t *key; + uint32_t cnt; }; static const uint8_t key[] = { @@ -54,7 +98,8 @@ static const struct test_data test_data_1 = { .msg = msg_1, .msg_len = 0, - .t = t_msg_1 + .t = t_msg_1, + .key = key, }; static const uint8_t msg_2[] = { @@ -70,7 +115,8 @@ static const struct test_data test_data_2 = { .msg = msg_2, .msg_len = 16, - .t = t_msg_2 + .t = t_msg_2, + .key = key, }; static const uint8_t msg_3[] = { @@ -87,7 +133,8 @@ static const struct test_data test_data_3 = { .msg = msg_3, .msg_len = 40, - .t = t_msg_3 + .t = t_msg_3, + .key = key, }; static const uint8_t msg_4[] = { @@ -106,13 +153,31 @@ static const struct test_data test_data_4 = { .msg = msg_4, .msg_len = 64, - .t = t_msg_4 + .t = t_msg_4, + .key = key, }; -static void print_debug(const char *str, void *user_data) -{ - tester_debug("%s", str); -} +static const uint8_t msg_5[] = { + 0xd2, 0x12, 0x00, 0x13, 0x37 +}; + +static const uint8_t key_5[] = { + 0x50, 0x5E, 0x42, 0xDF, 0x96, 0x91, 0xEC, 0x72, 0xD3, 0x1F, + 0xCD, 0xFB, 0xEB, 0x64, 0x1B, 0x61 +}; + +static const uint8_t t_msg_5[] = { + 0x01, 0x00, 0x00, 0x00, 0xF1, 0x87, 0x1E, 0x93, 0x3C, 0x90, + 0x0F, 0xf2 +}; + +static const struct test_data test_data_5 = { + .msg = msg_5, + .msg_len = sizeof(msg_5), + .t = t_msg_5, + .cnt = 1, + .key = key_5, +}; static bool result_compare(const uint8_t exp[12], uint8_t res[12]) { @@ -130,7 +195,7 @@ const struct test_data *d = data; memset(t, 0, 12); - if (!bt_crypto_sign_att(crypto, key, d->msg, d->msg_len, 0, t)) + if (!bt_crypto_sign_att(crypto, d->key, d->msg, d->msg_len, d->cnt, t)) g_assert(true); tester_debug("Result T:"); @@ -153,10 +218,13 @@ tester_init(&argc, &argv); + tester_add("/crypto/h6", NULL, NULL, test_h6, NULL); + tester_add("/crypto/sign_att_1", &test_data_1, NULL, test_sign, NULL); tester_add("/crypto/sign_att_2", &test_data_2, NULL, test_sign, NULL); tester_add("/crypto/sign_att_3", &test_data_3, NULL, test_sign, NULL); tester_add("/crypto/sign_att_4", &test_data_4, NULL, test_sign, NULL); + tester_add("/crypto/sign_att_5", &test_data_5, NULL, test_sign, NULL); exit_status = tester_run(); diff -Nru bluez-5.37/unit/test-gatt.c bluez-5.41/unit/test-gatt.c --- bluez-5.37/unit/test-gatt.c 2015-07-07 17:21:11.000000000 +0000 +++ bluez-5.41/unit/test-gatt.c 2016-05-26 16:51:11.000000000 +0000 @@ -209,7 +209,7 @@ #define PRIMARY_DISC_SMALL_DB \ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \ - raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x17, 0xF0, 0x00, 0x18, \ + raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x18, 0xF0, 0x00, 0x18, \ 0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x18) #define PRIMARY_DISC_LARGE_DB_1 \ @@ -223,7 +223,7 @@ 0x60, 0x00, 0x6B, 0x00, 0x0B, 0xA0), \ raw_pdu(0x10, 0x6C, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x06, 0x70, 0x00, 0x76, 0x00, 0x0B, 0xA0, \ - 0x80, 0x00, 0x85, 0x00, 0x0B, 0xA0), \ + 0x80, 0x00, 0x86, 0x00, 0x0B, 0xA0), \ raw_pdu(0x10, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28), \ raw_pdu(0x11, 0x14, 0x90, 0x00, 0x96, 0x00, \ 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, \ @@ -244,29 +244,33 @@ raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a) #define INCLUDE_DISC_SMALL_DB \ - raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x02, 0x28), \ + raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x02, 0x28), \ raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x0f, 0x00, \ 0x0a, 0x18), \ - raw_pdu(0x08, 0x12, 0xf0, 0x17, 0xf0, 0x02, 0x28), \ + raw_pdu(0x08, 0x12, 0xf0, 0x18, 0xf0, 0x02, 0x28), \ raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a), \ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), \ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a) #define CHARACTERISTIC_DISC_SMALL_DB \ - raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28), \ + raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x03, 0x28), \ raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, \ 0x2a), \ - raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28), \ - raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef, \ + raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28), \ + raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef, \ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, \ 0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00), \ - raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28), \ - raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01, \ + raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28), \ + raw_pdu(0x09, 0x07, 0x17, 0xf0, 0x02, 0x18, 0xf0, 0x01, \ 0x2a), \ - raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28), \ - raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a), \ + raw_pdu(0x08, 0x18, 0xf0, 0x18, 0xf0, 0x03, 0x28), \ + raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a), \ + raw_pdu(0x04, 0x16, 0xf0, 0x16, 0xf0), \ + raw_pdu(0x05, 0x01, 0x16, 0xf0, 0x00, 0x29), \ + raw_pdu(0x0a, 0x16, 0xf0), \ + raw_pdu(0x0b, 0x01, 0x00), \ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), \ - raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29, \ + raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, \ 0x2a), \ raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), \ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a) @@ -524,9 +528,9 @@ bt_uuid_t a_uuid, b_uuid; gatt_db_attribute_get_char_data(a, &a_handle, &a_value_handle, - &a_properties, &a_uuid); + &a_properties, NULL, &a_uuid); gatt_db_attribute_get_char_data(b, &b_handle, &b_value_handle, - &b_properties, &b_uuid); + &b_properties, NULL, &b_uuid); return a_handle == b_handle && a_value_handle == b_value_handle && a_properties == b_properties && @@ -1554,20 +1558,27 @@ BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_NOTIFY | - BT_GATT_CHRC_PROP_INDICATE, + BT_GATT_CHRC_PROP_INDICATE | + BT_GATT_CHRC_PROP_EXT_PROP, "BlueZ"), DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x00, 0x00), DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ, "Manufacturer Name"), - PRIMARY_SERVICE(0xF010, GAP_UUID, 8), + DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01, + 0x00), + + PRIMARY_SERVICE(0xF010, GAP_UUID, 9), INCLUDE(0x0001), CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, "BlueZ Unit Tester"), CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, - BT_GATT_CHRC_PROP_READ, 0x09), + BT_GATT_CHRC_PROP_READ | + BT_GATT_CHRC_PROP_EXT_PROP, 0x09), + DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01, + 0x00), CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, 0x00, 0x00), PRIMARY_SERVICE(0xFFFF, DEVICE_INFORMATION_UUID, 1), @@ -1607,15 +1618,18 @@ static struct gatt_db *make_test_spec_large_db_1(void) { const struct att_handle_spec specs[] = { - PRIMARY_SERVICE(0x0080, "a00b", 6), + PRIMARY_SERVICE(0x0080, "a00b", 7), CHARACTERISTIC(0xb008, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ | - BT_GATT_CHRC_PROP_WRITE, + BT_GATT_CHRC_PROP_WRITE | + BT_GATT_CHRC_PROP_EXT_PROP, 0x08), DESCRIPTOR(0xb015, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x01), DESCRIPTOR(0xb016, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x02), DESCRIPTOR(0xb017, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE | BT_ATT_PERM_ENCRYPT, 0x03), + DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01, + 0x00), SECONDARY_SERVICE(0x0001, "a00d", 6), INCLUDE(0x0080), @@ -2419,7 +2433,7 @@ raw_pdu(0x03, 0x00, 0x02), raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00, 0x18), - raw_pdu(0x07, 0x10, 0xf0, 0x17, 0xf0), + raw_pdu(0x07, 0x10, 0xf0, 0x18, 0xf0), raw_pdu(0x06, 0x18, 0xf0, 0xff, 0xff, 0x00, 0x28, 0x00, 0x18), raw_pdu(0x01, 0x06, 0x18, 0xf0, 0x0a)); @@ -2431,7 +2445,7 @@ 0xa0), raw_pdu(0x07, 0x30, 0x00, 0x32, 0x00, 0x50, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6b, 0x00, 0x70, 0x00, 0x76, - 0x00, 0x80, 0x00, 0x85, 0x00), + 0x00, 0x80, 0x00, 0x86, 0x00), raw_pdu(0x06, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0b, 0xa0), raw_pdu(0x01, 0x06, 0x86, 0x00, 0x0a)); @@ -2507,7 +2521,7 @@ ts_large_db_1, NULL, raw_pdu(0x03, 0x00, 0x02), raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28), - raw_pdu(0x09, 0x08, 0x02, 0x00, 0x80, 0x00, 0x85, 0x00, + raw_pdu(0x09, 0x08, 0x02, 0x00, 0x80, 0x00, 0x86, 0x00, 0x0b, 0xa0, 0x21, 0x00, 0x01, 0x00, 0x06, 0x00, 0x0d, 0xa0), raw_pdu(0x08, 0x22, 0x00, 0xff, 0xff, 0x02, 0x28), @@ -2533,25 +2547,25 @@ define_test_server("/TP/GAD/SR/BV-04-C/small/1", test_server, ts_small_db, NULL, raw_pdu(0x03, 0x00, 0x02), - raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28), + raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x03, 0x28), raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, 0x2a), - raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef, + raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28), + raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb0, 0x00, 0x00), - raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01, + raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28), + raw_pdu(0x09, 0x07, 0x17, 0xf0, 0x02, 0x18, 0xf0, 0x01, 0x2a), - raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a)); + raw_pdu(0x08, 0x18, 0xf0, 0x18, 0xf0, 0x03, 0x28), + raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a)); define_test_server("/TP/GAD/SR/BV-04-C/small/2", test_server, ts_small_db, NULL, raw_pdu(0x03, 0x00, 0x02), raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28), - raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29, + raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, 0x2a), raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)); @@ -2590,21 +2604,21 @@ raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, 0x2a), raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef, + raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x82, 0x15, 0xf0, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0xb0, 0x00, 0x00), - raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01, + raw_pdu(0x08, 0x15, 0xf0, 0x18, 0xf0, 0x03, 0x28), + raw_pdu(0x09, 0x07, 0x17, 0xf0, 0x02, 0x18, 0xf0, 0x01, 0x2a), - raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28), - raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a)); + raw_pdu(0x08, 0x18, 0xf0, 0x18, 0xf0, 0x03, 0x28), + raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a)); define_test_server("/TP/GAD/SR/BV-05-C/small/2", test_server, ts_small_db, NULL, raw_pdu(0x03, 0x00, 0x02), raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28), - raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29, + raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, 0x2a), raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28), raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)); @@ -2798,7 +2812,7 @@ 0x00), raw_pdu(0x09, 0x03, 0x15, 0xF0, 0x09), raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x2a), - raw_pdu(0x09, 0x04, 0x17, 0xF0, 0x00, 0x00)); + raw_pdu(0x09, 0x04, 0x18, 0xF0, 0x00, 0x00)); define_test_server("/TP/GAR/SR/BV-03-C/large-1", test_server, ts_large_db_1, NULL, @@ -2892,14 +2906,14 @@ define_test_client("/TP/GAR/CL/BV-04-C", test_client, service_db_1, &test_long_read_1, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x0d, 0x01, 0x02, 0x03)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x0b, 0x01, 0x02, 0x03)); define_test_client("/TP/GAR/CL/BV-04-C/512B", test_client, service_db_1, &test_long_read_2, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff, + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -3014,46 +3028,46 @@ define_test_client("/TP/GAR/CL/BI-12-C", test_client, service_db_1, &test_long_read_3, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x02)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x02)); define_test_client("/TP/GAR/CL/BI-13-C", test_client, service_db_1, &test_long_read_4, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x07)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x07)); define_test_client("/TP/GAR/CL/BI-14-C", test_client, service_db_1, &test_long_read_5, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01)); + raw_pdu(0x0a, 0x00, 0x00), + raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01)); define_test_client("/TP/GAR/CL/BI-15-C", test_client, service_db_1, &test_long_read_6, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x08)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08)); define_test_client("/TP/GAR/CL/BI-16-C", test_client, service_db_1, &test_long_read_7, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05)); define_test_client("/TP/GAR/CL/BI-16-C/auto", test_client, service_db_1, &test_long_read_1, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05), - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05), + raw_pdu(0x0a, 0x03, 0x00), raw_pdu(0x0d, 0x01, 0x02, 0x03)); define_test_client("/TP/GAR/CL/BI-17-C", test_client, service_db_1, &test_long_read_8, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x0c)); + raw_pdu(0x0a, 0x03, 0x00), + raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x0c)); define_test_client("/TP/GAR/CL/BI-18-C", test_client, service_db_1, &test_multiple_read_2, @@ -3299,14 +3313,14 @@ define_test_client("/TP/GAR/CL/BV-07-C", test_client, service_db_1, &test_long_read_9, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x0d, 0x01, 0x02, 0x03)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x0b, 0x01, 0x02, 0x03)); define_test_client("/TP/GAR/CL/BV-07-C/512B", test_client, service_db_1, &test_long_read_10, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff, + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -3415,46 +3429,46 @@ define_test_client("/TP/GAR/CL/BI-28-C", test_client, service_db_1, &test_long_read_11, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x02)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x02)); define_test_client("/TP/GAR/CL/BI-29-C", test_client, service_db_1, &test_long_read_12, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x07)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x07)); define_test_client("/TP/GAR/CL/BI-30-C", test_client, service_db_1, &test_long_read_5, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01)); + raw_pdu(0x0a, 0x00, 0x00), + raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01)); define_test_client("/TP/GAR/CL/BI-31-C", test_client, service_db_1, &test_long_read_13, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x08)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08)); define_test_client("/TP/GAR/CL/BI-32-C", test_client, service_db_1, &test_long_read_14, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05)); define_test_client("/TP/GAR/CL/BI-32-C/auto", test_client, service_db_1, &test_long_read_9, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05), - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x0d, 0x01, 0x02, 0x03)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05), + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x0b, 0x01, 0x02, 0x03)); define_test_client("/TP/GAR/CL/BI-33-C", test_client, service_db_1, &test_long_read_15, SERVICE_DATA_1_PDUS, - raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00), - raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x0c)); + raw_pdu(0x0a, 0x04, 0x00), + raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x0c)); define_test_client("/TP/GAR/CL/BI-34-C", test_client, service_db_1, &test_read_12, @@ -3852,8 +3866,8 @@ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), - raw_pdu(0x16, 0x03, 0x00, 0x3f, 0x00, 0xff), - raw_pdu(0x17, 0x03, 0x00, 0x3f, 0x00, 0xff), + raw_pdu(0x16, 0x03, 0x00, 0x12, 0x00, 0xff), + raw_pdu(0x17, 0x03, 0x00, 0x12, 0x00, 0xff), raw_pdu(0x18, 0x01), raw_pdu(0x19)); @@ -3866,8 +3880,8 @@ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), - raw_pdu(0x16, 0x82, 0x00, 0x3f, 0x00, 0xff), - raw_pdu(0x17, 0x82, 0x00, 0x3f, 0x00, 0xff), + raw_pdu(0x16, 0x82, 0x00, 0x12, 0x00, 0xff), + raw_pdu(0x17, 0x82, 0x00, 0x12, 0x00, 0xff), raw_pdu(0x18, 0x01), raw_pdu(0x19)); @@ -3996,12 +4010,12 @@ raw_pdu(0x03, 0x00, 0x02), raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), - raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), - raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x16, 0x72, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x17, 0x72, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), - raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), - raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x16, 0x72, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x17, 0x72, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), raw_pdu(0x18, 0x01), raw_pdu(0x19)); @@ -4374,8 +4388,8 @@ raw_pdu(0x17, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), - raw_pdu(0x16, 0x04, 0x00, 0x3f, 0x00, 0xff), - raw_pdu(0x17, 0x04, 0x00, 0x3f, 0x00, 0xff), + raw_pdu(0x16, 0x04, 0x00, 0x12, 0x00, 0xff), + raw_pdu(0x17, 0x04, 0x00, 0x12, 0x00, 0xff), raw_pdu(0x18, 0x01), raw_pdu(0x19)); @@ -4388,8 +4402,8 @@ raw_pdu(0x17, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), - raw_pdu(0x16, 0x83, 0x00, 0x3f, 0x00, 0xff), - raw_pdu(0x17, 0x83, 0x00, 0x3f, 0x00, 0xff), + raw_pdu(0x16, 0x83, 0x00, 0x12, 0x00, 0xff), + raw_pdu(0x17, 0x83, 0x00, 0x12, 0x00, 0xff), raw_pdu(0x18, 0x01), raw_pdu(0x19)); @@ -4425,5 +4439,19 @@ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff), raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03)); + define_test_server("/robustness/no-reliable-characteristic", + test_server, ts_large_db_1, NULL, + raw_pdu(0x03, 0x00, 0x02), + raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03), + raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06), + raw_pdu(0x18, 0x01), + raw_pdu(0x01, 0x18, 0x25, 0x00, 0x06)); + return tester_run(); }