[BlueZ upgrade 5.43 to 5.48] Patch 2/2 26/173026/1
authorAnupam Roy <anupam.r@samsung.com>
Mon, 19 Mar 2018 08:26:31 +0000 (13:56 +0530)
committerAnupam Roy <anupam.r@samsung.com>
Mon, 19 Mar 2018 08:26:31 +0000 (13:56 +0530)
Change-Id: I26e0fde334c2eac84d3f19370cab6dfbed073d2f
Signed-off-by: Anupam Roy <anupam.r@samsung.com>
198 files changed:
AUTHORS
ChangeLog
HACKING
Makefile.am
Makefile.plugins
Makefile.tools
README
android/Android.mk
android/avdtp.c
android/bas.c [deleted file]
android/bluetooth.c
android/bluetooth.h
android/dis.c [deleted file]
android/gatt.c
android/hidhost.c
android/hog.c [deleted file]
android/scpp.c [deleted file]
android/scpp.h [deleted file]
android/tester-bluetooth.c
android/tester-gatt.c
android/tester-main.c
bootstrap-configure
client/advertising.c
client/advertising.h
client/agent.c
client/agent.h
client/display.c
client/display.h
client/gatt.c
client/gatt.h
client/main.c
configure.ac
doc/adapter-api.txt
doc/advertising-api.txt
doc/agent-api.txt
doc/battery-api.txt [new file with mode: 0644]
doc/device-api.txt
doc/gatt-api.txt
doc/mgmt-api.txt
doc/oob-api.txt [deleted file]
doc/test-runner.txt
emulator/btdev.c
emulator/le.c
gdbus/client.c
gdbus/object.c
gobex/gobex-apparam.c
gobex/gobex-packet.c
gobex/gobex-packet.h
gobex/gobex-transfer.c
gobex/gobex.c
gobex/gobex.h
lib/hci.c
lib/sdp.h
lib/uuid.h
mesh/README [new file with mode: 0644]
mesh/agent.c [new file with mode: 0644]
mesh/agent.h [moved from android/hog.h with 55% similarity, mode: 0644]
mesh/config-client.c [new file with mode: 0644]
mesh/config-model.h [new file with mode: 0644]
mesh/config-server.c [new file with mode: 0644]
mesh/crypto.c [new file with mode: 0644]
mesh/crypto.h [new file with mode: 0644]
mesh/gatt.c [new file with mode: 0644]
mesh/gatt.h [new file with mode: 0644]
mesh/keys.h [new file with mode: 0644]
mesh/local_node.json [new file with mode: 0644]
mesh/main.c [new file with mode: 0644]
mesh/mesh-net.h [new file with mode: 0644]
mesh/net.c [new file with mode: 0644]
mesh/net.h [new file with mode: 0644]
mesh/node.c [new file with mode: 0644]
mesh/node.h [new file with mode: 0644]
mesh/onoff-model.c [new file with mode: 0644]
mesh/onoff-model.h [moved from android/dis.h with 61% similarity, mode: 0644]
mesh/prov-db.c [new file with mode: 0644]
mesh/prov-db.h [new file with mode: 0644]
mesh/prov.c [new file with mode: 0644]
mesh/prov.h [moved from android/bas.h with 53% similarity, mode: 0644]
mesh/prov_db.json [new file with mode: 0644]
mesh/tags [new file with mode: 0644]
mesh/util.c [new file with mode: 0644]
mesh/util.h [new file with mode: 0644]
monitor/avctp.c
monitor/avdtp.c
monitor/bnep.c
monitor/broadcom.c [changed mode: 0755->0644]
monitor/bt.h
monitor/control.c
monitor/intel.c
monitor/l2cap.c
monitor/ll.c
monitor/lmp.c
monitor/main.c
monitor/packet.c
monitor/packet.h
monitor/rfcomm.c
monitor/sdp.c
obexd/client/bluetooth.c
obexd/plugins/bluetooth.c
obexd/plugins/messages-dummy.c
obexd/plugins/pbap.c
obexd/plugins/vcard.c
obexd/src/manager.c
obexd/src/obex.c
obexd/src/obex.h
packaging/bluez.spec
peripheral/gatt.c
plugins/autopair.c
plugins/dbusoob.c [deleted file]
plugins/neard.c
plugins/policy.c
plugins/sixaxis.c
profiles/alert/server.c
profiles/audio/a2dp.c
profiles/audio/avctp.c
profiles/audio/avdtp.c
profiles/audio/avrcp.c
profiles/audio/control.c
profiles/audio/player.c
profiles/audio/player.h
profiles/battery/battery.c [new file with mode: 0644]
profiles/deviceinfo/dis.c
profiles/deviceinfo/dis.h
profiles/gap/gas.c
profiles/input/device.c
profiles/input/hog-lib.c
profiles/input/hog-lib.h
profiles/input/hog.c
profiles/input/server.c
profiles/input/sixaxis.h [new file with mode: 0644]
profiles/midi/libmidi.c [new file with mode: 0644]
profiles/midi/libmidi.h [new file with mode: 0644]
profiles/midi/midi.c [new file with mode: 0644]
profiles/network/bnep.c
profiles/network/server.c
profiles/sap/sap-u8500.c [deleted file]
profiles/sap/server.c
profiles/scanparam/scan.c
src/adapter.c
src/adapter.h
src/advertising.c
src/bluetooth.conf
src/bluetoothd.8.in
src/device.c
src/device.h
src/gatt-client.c
src/gatt-database.c
src/hcid.h
src/main.c
src/main.conf
src/oob.c [deleted file]
src/oob.h [deleted file]
src/service.c
src/shared/ad.c
src/shared/ad.h
src/shared/att.c
src/shared/btp.c [new file with mode: 0644]
src/shared/btp.h [new file with mode: 0644]
src/shared/gatt-client.c
src/shared/gatt-client.h
src/shared/gatt-db.c
src/shared/gatt-db.h
src/shared/gatt-server.c
src/shared/gatt-server.h
src/shared/mainloop.c
src/shared/shell.c [new file with mode: 0644]
src/shared/shell.h [new file with mode: 0644]
src/shared/util.c
src/shared/util.h
test/advertisement-example [deleted file]
test/example-advertisement
test/example-gatt-server
tools/advtest.c [new file with mode: 0644]
tools/avtest.c
tools/bluemoon.c
tools/bluetooth-player.c
tools/btconfig.c [new file with mode: 0644]
tools/btgatt-client.c
tools/btmgmt.c
tools/btpclient.c [new file with mode: 0644]
tools/btproxy.c
tools/csr.c
tools/eddystone.c
tools/gatt-example [deleted file]
tools/hci-tester.c
tools/hciattach_st.c
tools/ibeacon.c
tools/mgmt-tester.c
tools/obex-client-tool.c
tools/obexctl.c
tools/parse_companies.pl
tools/parser/avdtp.c
tools/smp-tester.c
tools/test-runner.c
tools/update_compids.sh
unit/test-gatt.c
unit/test-hog.c
unit/test-midi.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 176b55b..df9cb96 100755 (executable)
--- a/AUTHORS
+++ b/AUTHORS
@@ -83,7 +83,7 @@ Alex Deymo <deymo@chromium.org>
 Petri Gynther <pgynther@google.com>
 Scott James Remnant <scott@netsplit.com>
 Jakub Tyszkowski <jakub.tyszkowski@tieto.com>
-Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@tieto.com>
+Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@codecoup.pl>
 Marcin Krąglak <marcin.kraglak@tieto.com>
 Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>
 Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
index 8e01791..b78d29a 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,65 @@
+ver 5.48:
+       Fix issue with subscriptions for unpaired devices.
+       Fix issue with handling A2DP and no available SEP.
+       Fix issue with handling AVCTP change path support.
+       Fix issue with handling AVCTP browsing channel.
+       Fix issue with handling AVCTP passthrough PDUs.
+       Fix issue with handling detaching of controller.
+       Fix issue with handling start discovery results.
+       Fix issue with handling non-connectable devices.
+       Fix issue with handling unused parameter in WriteValue.
+       Add support for service side AcquireWrite and AcquireNotify.
+       Add support for providing address type information.
+       Add support for cable based authentication and pairing.
+       Add support for Bluetooth Low-Energy battery service.
+       Add support for BTP client for qualification testing.
+       Add support for additional Mesh control functionality.
+       Mark advertising manager APIs as stable interfaces.
+
+ver 5.47:
+       Fix issue with handling AcquireNotify registration.
+       Fix issue with handling support for reconnection interval.
+       Fix issue with handling A2DP transport and accepting streams.
+       Fix issue with fallback from BR/EDR to LE bearer handling.
+       Add support for appearance and local name advertising data.
+       Add support for retrieving the supported discovery filters.
+       Add support for decoding Bluetooth 5.0 commands and events.
+       Add support for decoding Bluetooth Mesh advertising bearer.
+       Add support for Bluetooth Mesh control application.
+
+ver 5.46:
+       Fix issue with handling ATT over BR/EDR connections.
+       Fix issue with SDP browsing cleanup after connection.
+       Fix issue with pointer dereference and OPP Put request.
+       Fix issue with identity address updates during pairing.
+       Fix issue with not removing services that had disappeared.
+       Add support for improved discovery of included services.
+       Add support for simplified characteristics discovery.
+       Add support for GATT caching configuration option.
+       Add experimental support for AcquireWrite and AcquireNotify.
+
+ver 5.45:
+       Fix issue with agent support in Bluetooth client tool.
+       Fix issue with handling re-connection policy.
+       Fix issue with handling unknown ATT commands.
+       Fix issue with handling GATT Service Includes property.
+       Fix issue with handling PullAll for OBEX transfers.
+       Fix issue with handling delay in AVDTP Suspend responses.
+       Fix issue with handling decoding of management frames.
+       Add support for frame counters in Bluetooth monitor tool.
+
+ver 5.44:
+       Fix issue with GAP and GATT service registration.
+       Fix issue with wrong address type for ATT sockets.
+       Fix issue with dictionary entries for advertising.
+       Fix issue with device information and HID over GATT.
+       Fix issue with handling secondary service discovery.
+       Fix issue with handling Attribute Read Long procedure.
+       Fix issue with handling Attribute Write Long procedure.
+       Fix issue with handling abort of AVDTP SetConfiguration.
+       Add support for single-mode static address configuration.
+       Add support for MIDI over Bluetooth Low Energy.
+
 ver 5.43:
        Fix issue with HID over GATT support.
        Fix issue with ATT Find By Type response handling.
diff --git a/HACKING b/HACKING
index a8fb403..87762f1 100755 (executable)
--- a/HACKING
+++ b/HACKING
@@ -86,12 +86,12 @@ automatically includes this option.
     # sudo cp ./src/bluetooth.conf /etc/dbus-1/system.d/
 
   Run daemon in foreground with debugging
-    # sudo ./src/bluetoothd -n -d
+    # sudo ./src/bluetoothd -n -d -f ./src/main.conf
 
   Run daemon with valgrind
    # sudo valgrind --trace-children=yes --track-origins=yes --track-fds=yes \
    --show-possibly-lost=no --leak-check=full --suppressions=./tools/valgrind.supp \
-   ./src/bluetoothd -n -d
+   ./src/bluetoothd -n -d -f ./src/main.conf
 
 For production installations or distribution packaging it is important that
 the "--enable-maintainer-mode" option is NOT used.
@@ -108,6 +108,19 @@ If you fixed a bug or you want to add support for something, patches are
 welcome! In order to ease the inclusion of your patch, it's important to follow
 some rules, otherwise it will likely be rejected by maintainers.
 
+Make sure the author name and email are set properly:
+
+  # git config --global user.name <name>
+  # git config --global user.email <email>
+
+The preferred way to send patches is by email, using git send-email:
+
+  # git config --global sendemail.smtpencryption <tls>
+  # git config --global sendemail.smtpserver <smtp.gmail.com>
+  # git config --global sendemail.smtpuser <yourname@gmail.com>
+  # git config --global sendemail.smtpserverport <587>
+  # git config sendemail.to linux-bluetooth@vger.kernel.org
+
 BlueZ rules for submitting patches follow most of the rules used by Linux kernel
 (https://www.kernel.org/doc/Documentation/SubmittingPatches) with some remarks:
 
@@ -128,3 +141,17 @@ break compilation.
 should be limited to 50 characters and the description should be wrapped at 72
 characters except if it contains quoted information from debug tools like
 backtraces, compiler errors, etc.
+
+6) Prefix the email subject with [PATCH BlueZ]:
+
+  # git config format.subjectprefix "PATCH BlueZ"
+
+7) Add a cover letter when introducing a new feature explaning what problem
+you're trying to solve:
+
+  # git format-patch --cover-letter -M origin/master -o outgoing/
+  # edit outgoing/0000-*
+
+8) Submit:
+
+  # git send-email outgoing/*
index 7a8ad8d..676b931 100755 (executable)
@@ -80,7 +80,7 @@ include_HEADERS += $(lib_headers)
 lib_LTLIBRARIES += lib/libbluetooth.la
 
 lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:14:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:16:18
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -119,7 +119,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
                        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/tty.h
+                       src/shared/tty.h src/shared/shell.c src/shared/shell.h
 
 src_libshared_glib_la_SOURCES = $(shared_sources) \
                                src/shared/io-glib.c \
@@ -147,6 +147,7 @@ gobex_sources = gobex/gobex.h gobex/gobex.c \
 builtin_modules =
 builtin_sources =
 builtin_nodist =
+builtin_ldadd =
 
 include Makefile.plugins
 
@@ -193,6 +194,7 @@ src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
                        gdbus/libgdbus-internal.la \
                        src/libshared-glib.la \
                        @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl -lrt
+                       $(builtin_ldadd)
 src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
                                -Wl,--version-script=$(srcdir)/src/bluetooth.ver
 
@@ -437,6 +439,17 @@ noinst_PROGRAMS += $(unit_tests)
 endif
 
 TESTS = $(unit_tests)
+AM_TESTS_ENVIRONMENT = MALLOC_CHECK_=3 MALLOC_PERTURB_=69
+
+if DBUS_RUN_SESSION
+AM_TESTS_ENVIRONMENT += dbus-run-session --
+endif
+
+if VALGRIND
+LOG_COMPILER = valgrind --error-exitcode=1 --num-callers=30
+LOG_FLAGS = --trace-children=yes --leak-check=full --show-reachable=no \
+               --suppressions=$(srcdir)/tools/valgrind.supp --quiet
+endif
 
 pkgconfigdir = $(libdir)/pkgconfig
 
@@ -453,8 +466,13 @@ endif
 EXTRA_DIST += $(manual_pages:.1=.txt)
 
 DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
-                                       --enable-manpages \
-                                       --disable-systemd --disable-udev
+                                               --enable-health \
+                                               --enable-mesh \
+                                               --enable-midi \
+                                               --enable-manpages \
+                                               --enable-android \
+                                               --disable-systemd \
+                                               --disable-udev
 
 DISTCLEANFILES = $(pkgconfig_DATA) $(unit_tests) $(manual_pages)
 
@@ -482,7 +500,7 @@ $(lib_libbluetooth_la_OBJECTS): $(local_headers)
 
 lib/bluetooth/%.h: lib/%.h
        $(AM_V_at)$(MKDIR_P) lib/bluetooth
-       $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
+       $(AM_V_GEN)$(LN_S) -f $(abspath $<) $@
 
 if COVERAGE
 clean-coverage:
index 3e3644e..cce5cd8 100755 (executable)
@@ -16,11 +16,11 @@ builtin_modules += policy
 builtin_sources += plugins/policy.c
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-if TIZEN_BREDR_PLUGIN
-builtin_modules += dbusoob
-builtin_sources += plugins/dbusoob.c \
+#if TIZEN_BREDR_PLUGIN
+#builtin_modules += dbusoob
+#builtin_sources += plugins/dbusoob.c \
                        src/oob.h src/oob.c
-endif
+#endif
 #endif
 
 if TIZEN_NFC_PLUGIN
@@ -33,10 +33,10 @@ builtin_modules += sap
 builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
                        profiles/sap/manager.c profiles/sap/server.h \
                        profiles/sap/server.c profiles/sap/sap.h \
-                       profiles/sap/sap-u8500.c
+                       profiles/sap/sap-dummy.c
 
-#noinst_LIBRARIES += profiles/sap/libsap.a
-#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+noinst_LIBRARIES += profiles/sap/libsap.a
+profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
 endif
 
 if TIZEN_BREDR_PLUGIN
@@ -114,6 +114,14 @@ builtin_modules += deviceinfo
 builtin_sources += profiles/deviceinfo/deviceinfo.c
 endif
 
+if MIDI
+builtin_modules += midi
+builtin_sources += profiles/midi/midi.c \
+                        profiles/midi/libmidi.h \
+                        profiles/midi/libmidi.c
+builtin_ldadd += @ALSA_LIBS@
+endif
+
 if TIZEN_PROXIMITY_PLUGIN
 builtin_modules += proximity
 builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
index fe6220f..ae448d2 100755 (executable)
@@ -9,8 +9,30 @@ client_bluetoothctl_SOURCES = client/main.c \
                                        client/advertising.c \
                                        client/gatt.h client/gatt.c \
                                        monitor/uuid.h monitor/uuid.c
-client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
-                               -lreadline
+client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
+                               @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
+endif
+
+if MESH
+bin_PROGRAMS += mesh/meshctl
+
+mesh_meshctl_SOURCES = mesh/main.c \
+                                mesh/mesh-net.h \
+                                mesh/node.h mesh/node.c \
+                                mesh/gatt.h mesh/gatt.c \
+                                mesh/crypto.h mesh/crypto.c \
+                                mesh/keys.h \
+                                mesh/net.h mesh/net.c \
+                                mesh/prov.h mesh/prov.c \
+                                mesh/util.h mesh/util.c \
+                                mesh/agent.h mesh/agent.c \
+                                mesh/prov-db.h mesh/prov-db.c \
+                                mesh/config-model.h mesh/config-client.c \
+                                mesh/config-server.c \
+                                mesh/onoff-model.h mesh/onoff-model.c
+mesh_meshctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
+                                lib/libbluetooth-internal.la \
+                                @GLIB_LIBS@ @DBUS_LIBS@ -ljson-c -lreadline
 endif
 
 if MONITOR
@@ -370,11 +392,12 @@ tools_obex_server_tool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 tools_bluetooth_player_SOURCES = tools/bluetooth-player.c \
                                client/display.h client/display.c
 tools_bluetooth_player_LDADD = gdbus/libgdbus-internal.la \
+                               src/libshared-glib.la \
                                @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
 
 tools_obexctl_SOURCES = tools/obexctl.c \
                                client/display.h client/display.c
-tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
+tools_obexctl_LDADD = gdbus/libgdbus-internal.la src/libshared-glib.la \
                                @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
 
 tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
diff --git a/README b/README
index 7d1f5a1..f863a63 100755 (executable)
--- a/README
+++ b/README
@@ -186,6 +186,17 @@ For a working system, certain configuration options need to be enabled:
                The plugin is built into bluetoothd therefore it does not need
                to be package separately.
 
+       --enable-midi
+
+               This option enable MIDI support via ALSA Sequencer.
+
+               By default midi plugin is disabled since it still considered
+               experimental. When bluetoothd will create a new ALSA Sequencer
+               client and port for each device connected that supports the
+               MIDI GATT primary service.
+
+               The plugin is built into bluetoothd therefore it does not need
+               to be package separately.
 
 Information
 ===========
index 38ef4aa..76a826b 100755 (executable)
@@ -72,6 +72,7 @@ LOCAL_SRC_FILES := \
        bluez/src/shared/crypto.c \
        bluez/src/shared/uhid.c \
        bluez/src/shared/att.c \
+       bluez/src/shared/ad.c \
        bluez/src/sdpd-database.c \
        bluez/src/sdpd-service.c \
        bluez/src/sdpd-request.c \
index bab305b..34caf3d 100755 (executable)
@@ -2647,6 +2647,7 @@ static gboolean avdtp_parse_resp(struct avdtp *session,
                return avdtp_discover_resp(session, buf, size);
        case AVDTP_GET_ALL_CAPABILITIES:
                get_all = "ALL_";
+               /* fall through */
        case AVDTP_GET_CAPABILITIES:
                DBG("GET_%sCAPABILITIES request succeeded", get_all);
                if (!avdtp_get_capabilities_resp(session, buf, size))
diff --git a/android/bas.c b/android/bas.c
deleted file mode 100755 (executable)
index dcbf9de..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2014  Intel Corporation. All rights reserved.
- *
- *
- *  This library is free software; you can rebastribute 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 bastributed 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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#include "android/bas.h"
-
-#define ATT_NOTIFICATION_HEADER_SIZE 3
-#define ATT_READ_RESPONSE_HEADER_SIZE 1
-
-struct bt_bas {
-       int ref_count;
-       GAttrib *attrib;
-       struct gatt_primary *primary;
-       uint16_t handle;
-       uint16_t ccc_handle;
-       guint id;
-       struct queue *gatt_op;
-};
-
-struct gatt_request {
-       unsigned int id;
-       struct bt_bas *bas;
-       void *user_data;
-};
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
-       queue_remove(req->bas->gatt_op, req);
-       bt_bas_unref(req->bas);
-       free(req);
-}
-
-static void bas_free(struct bt_bas *bas)
-{
-       bt_bas_detach(bas);
-
-       g_free(bas->primary);
-       queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
-       g_free(bas);
-}
-
-struct bt_bas *bt_bas_new(void *primary)
-{
-       struct bt_bas *bas;
-
-       bas = g_try_new0(struct bt_bas, 1);
-       if (!bas)
-               return NULL;
-
-       bas->gatt_op = queue_new();
-       if (!bas->gatt_op) {
-               bas_free(bas);
-               return NULL;
-       }
-
-       if (primary)
-               bas->primary = g_memdup(primary, sizeof(*bas->primary));
-
-       return bt_bas_ref(bas);
-}
-
-struct bt_bas *bt_bas_ref(struct bt_bas *bas)
-{
-       if (!bas)
-               return NULL;
-
-       __sync_fetch_and_add(&bas->ref_count, 1);
-
-       return bas;
-}
-
-void bt_bas_unref(struct bt_bas *bas)
-{
-       if (!bas)
-               return;
-
-       if (__sync_sub_and_fetch(&bas->ref_count, 1))
-               return;
-
-       bas_free(bas);
-}
-
-static struct gatt_request *create_request(struct bt_bas *bas,
-                                                       void *user_data)
-{
-       struct gatt_request *req;
-
-       req = new0(struct gatt_request, 1);
-       if (!req)
-               return NULL;
-
-       req->user_data = user_data;
-       req->bas = bt_bas_ref(bas);
-
-       return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_bas *bas,
-                                               struct gatt_request *req,
-                                               unsigned int id)
-{
-       req->id = id;
-       return queue_push_head(bas->gatt_op, req);
-}
-
-static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
-                                       const uint8_t *value, size_t vlen,
-                                       GAttribResultFunc func,
-                                       gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(bas, user_data);
-       if (!req)
-               return;
-
-       id = gatt_write_char(attrib, handle, value, vlen, func, req);
-
-       if (set_and_store_gatt_req(bas, req, id))
-               return;
-
-       error("bas: Could not write characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-
-}
-
-static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
-                               GAttribResultFunc func, gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(bas, user_data);
-       if (!req)
-               return;
-
-       id = gatt_read_char(attrib, handle, func, req);
-
-       if (set_and_store_gatt_req(bas, req, id))
-               return;
-
-       error("bas: Could not read characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_char(struct bt_bas *bas, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                               bt_uuid_t *uuid, gatt_cb_t func,
-                                               gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(bas, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
-       if (set_and_store_gatt_req(bas, req, id))
-               return;
-
-       error("bas: Could not discover characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
-                               uint16_t start, uint16_t end, bt_uuid_t *uuid,
-                               gatt_cb_t func, gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(bas, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_desc(attrib, start, end, uuid, func, req);
-       if (set_and_store_gatt_req(bas, req, id))
-               return;
-
-       error("bas: Could not discover descriptor");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
-{
-       DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
-}
-
-static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                       gpointer user_data)
-{
-       DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
-}
-
-static void ccc_written_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_bas *bas = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Write Scan Refresh CCC failed: %s",
-                                               att_ecode2str(status));
-               return;
-       }
-
-       DBG("Battery Level: notification enabled");
-
-       bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
-                                       bas->handle, notification_cb, bas,
-                                       NULL);
-}
-
-static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
-                                                       void *user_data)
-{
-       uint8_t value[2];
-
-       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
-       write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
-                                                               user_data);
-}
-
-static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_bas *bas = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Error reading CCC value: %s", att_ecode2str(status));
-               return;
-       }
-
-       write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
-}
-
-static void discover_descriptor_cb(uint8_t status, GSList *descs,
-                                                               void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_bas *bas = req->user_data;
-       struct gatt_desc *desc;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Discover descriptors failed: %s", att_ecode2str(status));
-               return;
-       }
-
-       /* There will be only one descriptor on list and it will be CCC */
-       desc = descs->data;
-       bas->ccc_handle = desc->handle;
-
-       read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
-}
-
-static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_bas *bas = req->user_data;
-       struct gatt_char *chr;
-       uint16_t start, end;
-       bt_uuid_t uuid;
-
-       destroy_gatt_req(req);
-
-       if (status) {
-               error("Battery: %s", att_ecode2str(status));
-               return;
-       }
-
-       chr = chars->data;
-       bas->handle = chr->value_handle;
-
-       DBG("Battery handle: 0x%04x", bas->handle);
-
-       read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
-
-       start = chr->value_handle + 1;
-       end = bas->primary->range.end;
-
-       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
-       discover_desc(bas, bas->attrib, start, end, &uuid,
-                                               discover_descriptor_cb, bas);
-}
-
-bool bt_bas_attach(struct bt_bas *bas, void *attrib)
-{
-       if (!bas || bas->attrib || !bas->primary)
-               return false;
-
-       bas->attrib = g_attrib_ref(attrib);
-
-       if (bas->handle > 0)
-               return true;
-
-       discover_char(bas, bas->attrib, bas->primary->range.start,
-                                       bas->primary->range.end, NULL,
-                                       bas_discovered_cb, bas);
-
-       return true;
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
-       if (g_attrib_cancel(req->bas->attrib, req->id))
-               destroy_gatt_req(req);
-}
-
-void bt_bas_detach(struct bt_bas *bas)
-{
-       if (!bas || !bas->attrib)
-               return;
-
-       if (bas->id > 0) {
-               g_attrib_unregister(bas->attrib, bas->id);
-               bas->id = 0;
-       }
-
-       queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
-       g_attrib_unref(bas->attrib);
-       bas->attrib = NULL;
-}
index 51a31fe..7422ab9 100755 (executable)
@@ -42,6 +42,7 @@
 #include "src/shared/util.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/queue.h"
+#include "src/shared/ad.h"
 #include "src/eir.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
@@ -3265,7 +3266,11 @@ static void load_devices_info(bt_bluetooth_ready cb)
        load_irks(irks);
        g_slist_free_full(irks, g_free);
 
-       load_link_keys(keys, cb);
+       if (adapter.supported_settings & MGMT_SETTING_BREDR)
+               load_link_keys(keys, cb);
+       else
+               cb(0, &adapter.bdaddr);
+
        g_slist_free_full(keys, g_free);
 
        g_strfreev(devs);
@@ -3332,6 +3337,37 @@ static void clear_auto_connect_list(void)
        error("Could not clear auto connect list");
 }
 
+static void read_adv_features_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_rp_read_adv_features *rp = param;
+       bt_bluetooth_ready cb = user_data;
+       int err;
+
+       if (status) {
+               error("Failed to read advertising features for index %u: %s (0x%02x)",
+                               adapter.index, mgmt_errstr(status), status);
+               err = -EIO;
+               goto failed;
+       }
+
+       if (length < sizeof(*rp)) {
+               error("Too small read advertising features response");
+               err = -EIO;
+               goto failed;
+       }
+
+       adapter.max_advert_instance = rp->max_instances;
+       info("Max LE advertising instances: %d", adapter.max_advert_instance);
+
+       load_devices_info(cb);
+
+       return;
+
+failed:
+       cb(err, NULL);
+}
+
 static void read_info_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -3379,7 +3415,7 @@ static void read_info_complete(uint8_t status, uint16_t length,
 
        /* Store adapter information */
        adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
-                                               (rp->dev_class[2] << 16);
+               (rp->dev_class[2] << 16);
 
        adapter.supported_settings = le32_to_cpu(rp->supported_settings);
        adapter.current_settings = le32_to_cpu(rp->current_settings);
@@ -3395,7 +3431,7 @@ static void read_info_complete(uint8_t status, uint16_t length,
        enable_mps();
 
        missing_settings = adapter.current_settings ^
-                                               adapter.supported_settings;
+               adapter.supported_settings;
 
        if (missing_settings & MGMT_SETTING_SSP)
                set_mode(MGMT_OP_SET_SSP, 0x01);
@@ -3403,6 +3439,18 @@ static void read_info_complete(uint8_t status, uint16_t length,
        if (missing_settings & MGMT_SETTING_BONDABLE)
                set_mode(MGMT_OP_SET_BONDABLE, 0x01);
 
+       if (adapter.supported_settings & MGMT_SETTING_LE) {
+               if (mgmt_send(mgmt_if, MGMT_OP_READ_ADV_FEATURES, adapter.index,
+                                       0, NULL,
+                                       read_adv_features_complete, cb, NULL) == 0) {
+                       error("Cannot get LE adv features");
+                       err = -EIO;
+                       goto failed;
+               }
+       } else {
+               load_devices_info(cb);
+       }
+
        load_devices_info(cb);
        load_devices_cache();
 
@@ -3973,6 +4021,134 @@ bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
        return false;
 }
 
+struct addrm_adv_user_data {
+       bt_le_addrm_advertising_done cb;
+       void *user_data;
+};
+
+static void add_advertising_cb(uint8_t status, uint16_t length,
+                              const void *param, void *user_data)
+{
+       struct addrm_adv_user_data *data = user_data;
+
+       DBG("");
+
+       if (status)
+               error("Failed to add advertising %s (0x%02x))",
+                               mgmt_errstr(status), status);
+
+       data->cb(status, data->user_data);
+}
+
+bool bt_le_add_advertising(struct adv_instance *adv,
+                          bt_le_addrm_advertising_done cb, void *user_data)
+{
+       struct mgmt_cp_add_advertising *cp;
+       struct addrm_adv_user_data *cb_data;
+       size_t len;
+       size_t adv_data_len = 0;
+       size_t sr_data_len = 0;
+       uint8_t *dst, *adv_data, *sr_data;
+       bool ok = false;
+
+       /* These accept NULL and return NULL */
+       adv_data = bt_ad_generate(adv->ad, &adv_data_len);
+       sr_data = bt_ad_generate(adv->sr, &sr_data_len);
+
+       len = sizeof(*cp) + adv_data_len + sr_data_len;
+       cp = malloc0(len);
+       if (!cp)
+               goto out;
+
+       cp->instance = adv->instance;
+       cp->timeout = adv->timeout;
+       /* XXX: how should we set duration? (kernel defaults to 2s) */
+
+       switch (adv->type) {
+               case ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE:
+                       cp->flags |= MGMT_ADV_FLAG_CONNECTABLE;
+                       break;
+
+               case ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE:
+               case ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE:
+               default:
+                       break;
+       }
+
+       if (adv->include_tx_power)
+               cp->flags |= MGMT_ADV_FLAG_TX_POWER;
+
+       dst = cp->data;
+       if (adv_data) {
+               cp->adv_data_len = adv_data_len;
+               memcpy(dst, adv_data, adv_data_len);
+               dst += adv_data_len;
+       }
+
+       if (sr_data) {
+               cp->scan_rsp_len = sr_data_len;
+               memcpy(dst, sr_data, sr_data_len);
+               dst += sr_data_len;
+       }
+
+       DBG("lens: adv=%u sr=%u total=%zu",
+                       cp->adv_data_len, cp->scan_rsp_len, len);
+
+       cb_data = new0(typeof(*cb_data), 1);
+       cb_data->cb = cb;
+       cb_data->user_data = user_data;
+
+       ok = (mgmt_send(mgmt_if, MGMT_OP_ADD_ADVERTISING, adapter.index,
+                               len, cp, add_advertising_cb, cb_data, free) > 0);
+
+       if (!ok)
+               free(cb_data);
+
+out:
+       free(adv_data);
+       free(sr_data);
+       free(cp);
+
+       return ok;
+}
+
+static void remove_advertising_cb(uint8_t status, uint16_t length,
+                                 const void *param, void *user_data)
+{
+       struct addrm_adv_user_data *data = user_data;
+
+       DBG("");
+
+       if (status)
+               error("Failed to remove advertising %s (0x%02x))",
+                               mgmt_errstr(status), status);
+
+       data->cb(status, data->user_data);
+}
+
+bool bt_le_remove_advertising(struct adv_instance *adv,
+                             bt_le_addrm_advertising_done cb, void *user_data)
+{
+       struct mgmt_cp_remove_advertising cp = {
+               .instance = adv->instance,
+       };
+       struct addrm_adv_user_data *cb_data;
+       bool ok;
+
+       cb_data = new0(typeof(*cb_data), 1);
+       cb_data->cb = cb;
+       cb_data->user_data = user_data;
+
+       ok = (mgmt_send(mgmt_if, MGMT_OP_REMOVE_ADVERTISING, adapter.index,
+                               sizeof(cp), &cp,
+                               remove_advertising_cb, cb_data, free) > 0);
+
+       if (!ok)
+               free(cb_data);
+
+       return ok;
+}
+
 bool bt_le_register(bt_le_device_found cb)
 {
        if (gatt_device_found_cb)
index 4b17209..b139cb1 100755 (executable)
@@ -88,3 +88,28 @@ typedef void (*bt_paired_device_cb)(const bdaddr_t *addr);
 bool bt_paired_register(bt_paired_device_cb cb);
 void bt_paired_unregister(bt_paired_device_cb cb);
 bool bt_is_pairing(const bdaddr_t *addr);
+
+struct bt_ad;
+struct adv_instance {
+       uint8_t instance;
+       int32_t timeout;
+       int32_t type;
+       struct bt_ad *ad;
+       struct bt_ad *sr;
+       unsigned include_tx_power:1;
+};
+
+/* Values below have no C API definition - only in Java (AdvertiseManager.java)
+ * and bluedroid
+ */
+enum android_adv_type {
+       ANDROID_ADVERTISING_EVENT_TYPE_CONNECTABLE = 0,
+       ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE = 2,
+       ANDROID_ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3,
+};
+
+typedef void (*bt_le_addrm_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_add_advertising(struct adv_instance *adv,
+               bt_le_addrm_advertising_done cb, void *user_data);
+bool bt_le_remove_advertising(struct adv_instance *adv,
+               bt_le_addrm_advertising_done cb, void *user_data);
diff --git a/android/dis.c b/android/dis.c
deleted file mode 100755 (executable)
index 75dbe3d..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2012 Texas Instruments, Inc.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/gattrib.h"
-#include "attrib/att.h"
-#include "attrib/gatt.h"
-
-#include "android/dis.h"
-
-#define PNP_ID_SIZE    7
-
-struct bt_dis {
-       int                     ref_count;
-       uint16_t                handle;
-       uint8_t                 source;
-       uint16_t                vendor;
-       uint16_t                product;
-       uint16_t                version;
-       GAttrib                 *attrib;        /* GATT connection */
-       struct gatt_primary     *primary;       /* Primary details */
-       bt_dis_notify           notify;
-       void                    *notify_data;
-       struct queue            *gatt_op;
-};
-
-struct characteristic {
-       struct gatt_char        attr;   /* Characteristic */
-       struct bt_dis           *d;     /* deviceinfo where the char belongs */
-};
-
-struct gatt_request {
-       unsigned int id;
-       struct bt_dis *dis;
-       void *user_data;
-};
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
-       queue_remove(req->dis->gatt_op, req);
-       bt_dis_unref(req->dis);
-       free(req);
-}
-
-static void dis_free(struct bt_dis *dis)
-{
-       bt_dis_detach(dis);
-
-       g_free(dis->primary);
-       queue_destroy(dis->gatt_op, (void *) destroy_gatt_req);
-       g_free(dis);
-}
-
-struct bt_dis *bt_dis_new(void *primary)
-{
-       struct bt_dis *dis;
-
-       dis = g_try_new0(struct bt_dis, 1);
-       if (!dis)
-               return NULL;
-
-       dis->gatt_op = queue_new();
-       if (!dis->gatt_op) {
-               dis_free(dis);
-               return NULL;
-       }
-
-       if (primary)
-               dis->primary = g_memdup(primary, sizeof(*dis->primary));
-
-       return bt_dis_ref(dis);
-}
-
-struct bt_dis *bt_dis_ref(struct bt_dis *dis)
-{
-       if (!dis)
-               return NULL;
-
-       __sync_fetch_and_add(&dis->ref_count, 1);
-
-       return dis;
-}
-
-void bt_dis_unref(struct bt_dis *dis)
-{
-       if (!dis)
-               return;
-
-       if (__sync_sub_and_fetch(&dis->ref_count, 1))
-               return;
-
-       dis_free(dis);
-}
-
-static struct gatt_request *create_request(struct bt_dis *dis,
-                                                       void *user_data)
-{
-       struct gatt_request *req;
-
-       req = new0(struct gatt_request, 1);
-       if (!req)
-               return NULL;
-
-       req->user_data = user_data;
-       req->dis = bt_dis_ref(dis);
-
-       return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_dis *dis,
-                                               struct gatt_request *req,
-                                               unsigned int id)
-{
-       req->id = id;
-       return queue_push_head(dis->gatt_op, req);
-}
-
-static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_dis *dis = req->user_data;
-       uint8_t value[PNP_ID_SIZE];
-       ssize_t vlen;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Error reading PNP_ID value: %s", att_ecode2str(status));
-               return;
-       }
-
-       vlen = dec_read_resp(pdu, len, value, sizeof(value));
-       if (vlen < 0) {
-               error("Error reading PNP_ID: Protocol error");
-               return;
-       }
-
-       if (vlen < 7) {
-               error("Error reading PNP_ID: Invalid pdu length received");
-               return;
-       }
-
-       dis->source = value[0];
-       dis->vendor = get_le16(&value[1]);
-       dis->product = get_le16(&value[3]);
-       dis->version = get_le16(&value[5]);
-
-       DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X",
-                       dis->source, dis->vendor, dis->product, dis->version);
-
-       if (dis->notify)
-               dis->notify(dis->source, dis->vendor, dis->product,
-                                               dis->version, dis->notify_data);
-}
-
-static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle,
-                               GAttribResultFunc func, gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(dis, user_data);
-       if (!req)
-               return;
-
-       id = gatt_read_char(attrib, handle, func, req);
-
-       if (set_and_store_gatt_req(dis, req, id))
-               return;
-
-       error("dis: Could not read characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_char(struct bt_dis *dis, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                               bt_uuid_t *uuid, gatt_cb_t func,
-                                               gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(dis, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
-       if (set_and_store_gatt_req(dis, req, id))
-               return;
-
-       error("dis: Could not send discover characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
-                                                               void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_dis *d = req->user_data;
-       GSList *l;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Discover deviceinfo characteristics: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       for (l = characteristics; l; l = l->next) {
-               struct gatt_char *c = l->data;
-
-               if (strcmp(c->uuid, PNPID_UUID) == 0) {
-                       d->handle = c->value_handle;
-                       read_char(d, d->attrib, d->handle, read_pnpid_cb, d);
-                       break;
-               }
-       }
-}
-
-bool bt_dis_attach(struct bt_dis *dis, void *attrib)
-{
-       struct gatt_primary *primary = dis->primary;
-
-       if (dis->attrib || !primary)
-               return false;
-
-       dis->attrib = g_attrib_ref(attrib);
-
-       if (!dis->handle)
-               discover_char(dis, dis->attrib, primary->range.start,
-                                               primary->range.end, NULL,
-                                               configure_deviceinfo_cb, dis);
-
-       return true;
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
-       if (g_attrib_cancel(req->dis->attrib, req->id))
-               destroy_gatt_req(req);
-}
-
-void bt_dis_detach(struct bt_dis *dis)
-{
-       if (!dis->attrib)
-               return;
-
-       queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL);
-       g_attrib_unref(dis->attrib);
-       dis->attrib = NULL;
-}
-
-bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
-                                                       void *user_data)
-{
-       if (!dis)
-               return false;
-
-       dis->notify = func;
-       dis->notify_data = user_data;
-
-       return true;
-}
index 28635ed..032c109 100755 (executable)
@@ -46,6 +46,7 @@
 #include "src/shared/queue.h"
 #include "src/shared/att.h"
 #include "src/shared/gatt-db.h"
+#include "src/shared/ad.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
@@ -110,6 +111,8 @@ struct gatt_app {
        struct queue *notifications;
 
        gatt_conn_cb_t func;
+
+       struct adv_instance *adv;
 };
 
 struct element_id {
@@ -192,6 +195,7 @@ static struct ipc *hal_ipc = NULL;
 static bdaddr_t adapter_addr;
 static bool scanning = false;
 static unsigned int advertising_cnt = 0;
+static uint32_t adv_inst_bits = 0;
 
 static struct queue *gatt_apps = NULL;
 static struct queue *gatt_devices = NULL;
@@ -650,6 +654,19 @@ static void connection_cleanup(struct gatt_device *device)
                bt_auto_connect_remove(&device->bdaddr);
 }
 
+static void free_adv_instance(struct adv_instance *adv)
+{
+       if (!adv)
+               return;
+
+       if (adv->instance)
+               adv_inst_bits &= ~(1 << (adv->instance - 1));
+
+       bt_ad_unref(adv->ad);
+       bt_ad_unref(adv->sr);
+       free(adv);
+}
+
 static void destroy_gatt_app(void *data)
 {
        struct gatt_app *app = data;
@@ -674,6 +691,8 @@ static void destroy_gatt_app(void *data)
 
        queue_destroy(app->notifications, free);
 
+       free_adv_instance(app->adv);
+
        free(app);
 }
 
@@ -5586,19 +5605,162 @@ static void handle_client_set_scan_param(const void *buf, uint16_t len)
                                        HAL_STATUS_UNSUPPORTED);
 }
 
+static struct adv_instance *find_adv_instance(uint32_t client_if)
+{
+       struct gatt_app *app;
+       struct adv_instance *adv;
+       uint8_t inst = 0;
+       unsigned int i;
+
+       app = find_app_by_id(client_if);
+       if (!app)
+               return NULL;
+
+       if (app->adv)
+               return app->adv;
+
+       /* Assume that kernel supports <= 32 advertising instances (5 today)
+        * We have already indicated the number to the android framework layers
+        * via the LE features so we don't check again here.
+        * The kernel will detect the error if needed
+        */
+       for (i = 0; i < sizeof(adv_inst_bits) * 8; i++) {
+               uint32_t mask = 1 << i;
+
+               if (!(adv_inst_bits & mask)) {
+                       inst = i + 1;
+                       adv_inst_bits |= mask;
+                       break;
+               }
+       }
+       if (!inst)
+               return NULL;
+
+       adv = new0(typeof(*adv), 1);
+       adv->instance = inst;
+       app->adv = adv;
+
+       DBG("Assigned advertising instance %d for client %d", inst, client_if);
+
+       return adv;
+};
+
+/* Build advertising data object from a data buffer containing
+ * manufacturer_data, service_data, service uuids (in that order)
+ * The input data is raw with no TLV structure and the service uuids are 128 bit
+ */
+static struct bt_ad *build_adv_data(int32_t manufacturer_data_len,
+                                       int32_t service_data_len,
+                                       int32_t service_uuid_len,
+                                       const uint8_t *data_in)
+{
+       const int one_svc_uuid_len = 128 / 8;  /* Android uses 128bit UUIDs */
+       uint8_t *src = (uint8_t *)data_in;
+       struct bt_ad *ad;
+       unsigned num_svc_uuids, i;
+
+       ad = bt_ad_new();
+
+       if (manufacturer_data_len >= 2) { /* Includes manufacturer id */
+               uint16_t manufacturer_id;
+
+               manufacturer_id = bt_get_le16(src);
+               src += 2;
+
+               if (!bt_ad_add_manufacturer_data(ad,
+                                       manufacturer_id,
+                                       src,
+                                       manufacturer_data_len - 2))
+                       goto err;
+
+               src +=  manufacturer_data_len - 2;
+       }
+
+       if (service_data_len >= 2) { /* Includes service uuid (always 16 bit) */
+               bt_uuid_t bt_uuid;
+               uint16_t uuid16;
+
+               uuid16 = bt_get_le16(src);
+               src += 2;
+               bt_uuid16_create(&bt_uuid, uuid16);
+
+               if (!bt_ad_add_service_data(ad,
+                                       &bt_uuid,
+                                       src,
+                                       service_data_len - 2))
+                       goto err;
+
+               src += service_data_len - 2;
+       }
+
+       if (service_uuid_len % one_svc_uuid_len) {
+               error("Service UUIDs not multiple of %d bytes (%d)",
+                               one_svc_uuid_len, service_uuid_len);
+               num_svc_uuids = 0;
+       } else {
+               num_svc_uuids = service_uuid_len / one_svc_uuid_len;
+       }
+
+       for (i = 0; i  < num_svc_uuids; i++) {
+               bt_uuid_t bt_uuid;
+
+               android2uuid(src, &bt_uuid);
+               src += one_svc_uuid_len;
+
+               if (!bt_ad_add_service_uuid(ad, &bt_uuid))
+                       goto err;
+       }
+
+       return ad;
+
+err:
+       bt_ad_unref(ad);
+       return NULL;
+}
+
+
 static void handle_client_setup_multi_adv(const void *buf, uint16_t len)
 {
        const struct hal_cmd_gatt_client_setup_multi_adv *cmd = buf;
+       struct hal_ev_gatt_client_multi_adv_enable ev;
+       struct adv_instance *adv;
+       uint8_t status;
 
-       DBG("client_if %d", cmd->client_if);
+       DBG("client_if %d min_interval=%d max_interval=%d type=%d channel_map=0x%x tx_power=%d timeout=%d",
+               cmd->client_if,
+               cmd->min_interval,
+               cmd->max_interval,
+               cmd->type,
+               cmd->channel_map,
+               cmd->tx_power,
+               cmd->timeout);
+
+       adv = find_adv_instance(cmd->client_if);
+       if (!adv) {
+               status = HAL_STATUS_FAILED;
+               goto out;
+       }
 
-       /* TODO */
+       status = HAL_STATUS_SUCCESS;
+       adv->timeout = cmd->timeout;
+       adv->type = cmd->type;
+       if (adv->type != ANDROID_ADVERTISING_EVENT_TYPE_SCANNABLE) {
+               bt_ad_unref(adv->sr);
+               adv->sr = NULL;
+       }
 
+out:
        ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
                                        HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV,
-                                       HAL_STATUS_UNSUPPORTED);
+                                       status);
+
+       ev.client_if = cmd->client_if;
+       ev.status = status;
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_MULTI_ADV_ENABLE, sizeof(ev), &ev);
 }
 
+/* This is not currently called by Android 5.1 */
 static void handle_client_update_multi_adv(const void *buf, uint16_t len)
 {
        const struct hal_cmd_gatt_client_update_multi_adv *cmd = buf;
@@ -5612,30 +5774,157 @@ static void handle_client_update_multi_adv(const void *buf, uint16_t len)
                                        HAL_STATUS_UNSUPPORTED);
 }
 
+struct addrm_adv_cb_data {
+       int32_t client_if;
+       struct adv_instance *adv;
+};
+
+static void add_advertising_cb(uint8_t status, void *user_data)
+{
+       struct addrm_adv_cb_data *cb_data = user_data;
+       struct hal_ev_gatt_client_multi_adv_data ev = {
+               .status = status,
+               .client_if = cb_data->client_if,
+       };
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_MULTI_ADV_DATA,
+                       sizeof(ev), &ev);
+
+       free(cb_data);
+}
+
 static void handle_client_setup_multi_adv_inst(const void *buf, uint16_t len)
 {
        const struct hal_cmd_gatt_client_setup_multi_adv_inst *cmd = buf;
+       struct adv_instance *adv;
+       struct bt_ad *adv_data;
+       struct addrm_adv_cb_data *cb_data = NULL;
+       uint8_t status = HAL_STATUS_FAILED;
+
+       DBG("client_if %d set_scan_rsp=%d include_name=%d include_tx_power=%d appearance=%d manuf_data_len=%d svc_data_len=%d svc_uuid_len=%d",
+                       cmd->client_if,
+                       cmd->set_scan_rsp,
+                       cmd->include_name,
+                       cmd->include_tx_power,
+                       cmd->appearance,
+                       cmd->manufacturer_data_len,
+                       cmd->service_data_len,
+                       cmd->service_uuid_len
+          );
+
+       adv = find_adv_instance(cmd->client_if);
+       if (!adv)
+               goto out;
+
+       adv->include_tx_power = cmd->include_tx_power ? 1 : 0;
+
+       adv_data = build_adv_data(cmd->manufacturer_data_len,
+                       cmd->service_data_len,
+                       cmd->service_uuid_len,
+                       cmd->data_service_uuid);
+       if (!adv_data)
+               goto out;
+
+       if (cmd->set_scan_rsp) {
+               bt_ad_unref(adv->sr);
+               adv->sr = adv_data;
+       } else {
+               bt_ad_unref(adv->ad);
+               adv->ad = adv_data;
+       }
 
-       DBG("client_if %d", cmd->client_if);
+       cb_data = new0(typeof(*cb_data), 1);
+       cb_data->client_if = cmd->client_if;
+       cb_data->adv = adv;
 
-       /* TODO */
+       if (!bt_le_add_advertising(adv, add_advertising_cb, cb_data)) {
+               error("gatt: Could not add advertising");
+               free(cb_data);
+               goto out;
+       }
 
+       status = HAL_STATUS_SUCCESS;
+
+out:
        ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
-                                       HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
-                                       HAL_STATUS_UNSUPPORTED);
+                       HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST,
+                       status);
+
+       if (status != HAL_STATUS_SUCCESS) {
+               struct hal_ev_gatt_client_multi_adv_data ev = {
+                       .status = status,
+                       .client_if = cmd->client_if,
+               };
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_CLIENT_MULTI_ADV_DATA,
+                               sizeof(ev), &ev);
+       }
+}
+
+static void remove_advertising_cb(uint8_t status, void *user_data)
+{
+       struct addrm_adv_cb_data *cb_data = user_data;
+       struct hal_ev_gatt_client_multi_adv_data ev = {
+               .status = status,
+               .client_if = cb_data->client_if,
+       };
+
+       free_adv_instance(cb_data->adv);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE,
+                       sizeof(ev), &ev);
+
+       free(cb_data);
 }
 
 static void handle_client_disable_multi_adv_inst(const void *buf, uint16_t len)
 {
        const struct hal_cmd_gatt_client_disable_multi_adv_inst *cmd = buf;
+       struct adv_instance *adv;
+       struct gatt_app *app;
+       struct addrm_adv_cb_data *cb_data = NULL;
+       uint8_t status = HAL_STATUS_FAILED;
 
        DBG("client_if %d", cmd->client_if);
 
-       /* TODO */
+       adv = find_adv_instance(cmd->client_if);
+       if (!adv)
+               goto out;
+
+       cb_data = new0(typeof(*cb_data), 1);
+       cb_data->client_if = cmd->client_if;
+       cb_data->adv = adv;
+
+       if (!bt_le_remove_advertising(adv, remove_advertising_cb, cb_data)) {
+               error("gatt: Could not remove advertising");
+               free(cb_data);
+               goto out;
+       }
+
+       app = find_app_by_id(cmd->client_if);
+       if (app)
+               app->adv = NULL;
 
+       status = HAL_STATUS_SUCCESS;
+
+out:
        ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
-                               HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
-                               HAL_STATUS_UNSUPPORTED);
+                       HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST,
+                       status);
+
+       if (status != HAL_STATUS_SUCCESS) {
+               struct hal_ev_gatt_client_multi_adv_data ev = {
+                       .status = status,
+                       .client_if = cmd->client_if,
+               };
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_CLIENT_MULTI_ADV_DISABLE,
+                               sizeof(ev), &ev);
+       }
 }
 
 static void handle_client_configure_batchscan(const void *buf, uint16_t len)
@@ -5824,7 +6113,7 @@ static const struct ipc_handler cmd_handlers[] = {
        { handle_client_update_multi_adv, false,
                sizeof(struct hal_cmd_gatt_client_update_multi_adv) },
        /* HAL_OP_GATT_CLIENT_SETUP_MULTI_ADV_INST */
-       { handle_client_setup_multi_adv_inst, false,
+       { handle_client_setup_multi_adv_inst, true,
                sizeof(struct hal_cmd_gatt_client_setup_multi_adv_inst) },
        /* HAL_OP_GATT_CLIENT_DISABLE_MULTI_ADV_INST */
        { handle_client_disable_multi_adv_inst, false,
index 591ca95..fe0ea2f 100755 (executable)
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
+#include "lib/uuid.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/util.h"
 #include "src/shared/uhid.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
 #include "src/sdp-client.h"
 #include "src/uuid-helper.h"
 #include "src/log.h"
diff --git a/android/hog.c b/android/hog.c
deleted file mode 100755 (executable)
index ff77bb3..0000000
+++ /dev/null
@@ -1,1511 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2014  Intel Corporation.
- *  Copyright (C) 2012  Marcel Holtmann <marcel@holtmann.org>
- *  Copyright (C) 2012  Nordic Semiconductor Inc.
- *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <glib.h>
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/uhid.h"
-#include "src/shared/queue.h"
-#include "src/log.h"
-
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "attrib/gatt.h"
-
-#include "btio/btio.h"
-
-#include "android/scpp.h"
-#include "android/dis.h"
-#include "android/bas.h"
-#include "android/hog.h"
-
-#define HOG_UUID               "00001812-0000-1000-8000-00805f9b34fb"
-
-#define HOG_INFO_UUID          0x2A4A
-#define HOG_REPORT_MAP_UUID    0x2A4B
-#define HOG_REPORT_UUID                0x2A4D
-#define HOG_PROTO_MODE_UUID    0x2A4E
-#define HOG_CONTROL_POINT_UUID 0x2A4C
-
-#define HOG_REPORT_TYPE_INPUT  1
-#define HOG_REPORT_TYPE_OUTPUT 2
-#define HOG_REPORT_TYPE_FEATURE        3
-
-#define HOG_PROTO_MODE_BOOT    0
-#define HOG_PROTO_MODE_REPORT  1
-
-#define HOG_REPORT_MAP_MAX_SIZE        512
-#define HID_INFO_SIZE                  4
-#define ATT_NOTIFICATION_HEADER_SIZE   3
-
-struct bt_hog {
-       int                     ref_count;
-       char                    *name;
-       uint16_t                vendor;
-       uint16_t                product;
-       uint16_t                version;
-       struct gatt_primary     *primary;
-       GAttrib                 *attrib;
-       GSList                  *reports;
-       struct bt_uhid          *uhid;
-       int                     uhid_fd;
-       gboolean                has_report_id;
-       uint16_t                bcdhid;
-       uint8_t                 bcountrycode;
-       uint16_t                proto_mode_handle;
-       uint16_t                ctrlpt_handle;
-       uint8_t                 flags;
-       unsigned int            getrep_att;
-       uint16_t                getrep_id;
-       unsigned int            setrep_att;
-       uint16_t                setrep_id;
-       struct bt_scpp          *scpp;
-       struct bt_dis           *dis;
-       struct queue            *bas;
-       GSList                  *instances;
-       struct queue            *gatt_op;
-};
-
-struct report {
-       struct bt_hog           *hog;
-       uint8_t                 id;
-       uint8_t                 type;
-       uint16_t                ccc_handle;
-       guint                   notifyid;
-       struct gatt_char        *decl;
-       uint16_t                len;
-       uint8_t                 *value;
-};
-
-struct gatt_request {
-       unsigned int id;
-       struct bt_hog *hog;
-       void *user_data;
-};
-
-static struct gatt_request *create_request(struct bt_hog *hog,
-                                                       void *user_data)
-{
-       struct gatt_request *req;
-
-       req = new0(struct gatt_request, 1);
-       if (!req)
-               return NULL;
-
-       req->user_data = user_data;
-       req->hog = bt_hog_ref(hog);
-
-       return req;
-}
-
-static bool set_and_store_gatt_req(struct bt_hog *hog,
-                                               struct gatt_request *req,
-                                               unsigned int id)
-{
-       req->id = id;
-       return queue_push_head(hog->gatt_op, req);
-}
-
-static void destroy_gatt_req(struct gatt_request *req)
-{
-       queue_remove(req->hog->gatt_op, req);
-       bt_hog_unref(req->hog);
-       free(req);
-}
-
-static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
-                                       const uint8_t *value, size_t vlen,
-                                       GAttribResultFunc func,
-                                       gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_write_char(attrib, handle, value, vlen, func, req);
-
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("hog: Could not read char");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
-                               GAttribResultFunc func, gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_read_char(attrib, handle, func, req);
-
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("hog: Could not read char");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
-                               uint16_t start, uint16_t end, gatt_cb_t func,
-                               gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_desc(attrib, start, end, NULL, func, req);
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("hog: Could not discover descriptors");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_char(struct bt_hog *hog, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                               bt_uuid_t *uuid, gatt_cb_t func,
-                                               gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_char(attrib, start, end, uuid, func, req);
-
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("hog: Could not discover characteristic");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
-                                               bt_uuid_t *uuid, gatt_cb_t func,
-                                               gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_discover_primary(attrib, uuid, func, req);
-
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("hog: Could not send discover primary");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void find_included(struct bt_hog *hog, GAttrib *attrib,
-                                       uint16_t start, uint16_t end,
-                                       gatt_cb_t func, gpointer user_data)
-{
-       struct gatt_request *req;
-       unsigned int id;
-
-       req = create_request(hog, user_data);
-       if (!req)
-               return;
-
-       id = gatt_find_included(attrib, start, end, func, req);
-
-       if (set_and_store_gatt_req(hog, req, id))
-               return;
-
-       error("Could not find included");
-       g_attrib_cancel(attrib, id);
-       free(req);
-}
-
-static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
-{
-       struct report *report = user_data;
-       struct bt_hog *hog = report->hog;
-       struct uhid_event ev;
-       uint8_t *buf;
-       int err;
-
-       if (len < ATT_NOTIFICATION_HEADER_SIZE) {
-               error("Malformed ATT notification");
-               return;
-       }
-
-       pdu += ATT_NOTIFICATION_HEADER_SIZE;
-       len -= ATT_NOTIFICATION_HEADER_SIZE;
-
-       memset(&ev, 0, sizeof(ev));
-       ev.type = UHID_INPUT;
-       buf = ev.u.input.data;
-
-       if (hog->has_report_id) {
-               buf[0] = report->id;
-               len = MIN(len, sizeof(ev.u.input.data) - 1);
-               memcpy(buf + 1, pdu, len);
-               ev.u.input.size = ++len;
-       } else {
-               len = MIN(len, sizeof(ev.u.input.data));
-               memcpy(buf, pdu, len);
-               ev.u.input.size = len;
-       }
-
-       err = bt_uhid_send(hog->uhid, &ev);
-       if (err < 0) {
-               error("bt_uhid_send: %s (%d)", strerror(-err), -err);
-               return;
-       }
-
-       DBG("HoG report (%u bytes)", ev.u.input.size);
-}
-
-static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct report *report = req->user_data;
-       struct bt_hog *hog = report->hog;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Write report characteristic descriptor failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       report->notifyid = g_attrib_register(hog->attrib,
-                                       ATT_OP_HANDLE_NOTIFY,
-                                       report->decl->value_handle,
-                                       report_value_cb, report, NULL);
-
-       DBG("Report characteristic descriptor written: notifications enabled");
-}
-
-static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
-                                                       void *user_data)
-{
-       uint8_t value[2];
-
-       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
-       write_char(hog, attrib, handle, value, sizeof(value),
-                                       report_ccc_written_cb, user_data);
-}
-
-static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct report *report = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Error reading CCC value: %s", att_ecode2str(status));
-               return;
-       }
-
-       write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report);
-}
-
-static void report_reference_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct report *report = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Read Report Reference descriptor failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       if (plen != 3) {
-               error("Malformed ATT read response");
-               return;
-       }
-
-       report->id = pdu[1];
-       report->type = pdu[2];
-       DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
-
-       /* Enable notifications only for Input Reports */
-       if (report->type == HOG_REPORT_TYPE_INPUT)
-               read_char(report->hog, report->hog->attrib, report->ccc_handle,
-                                                       ccc_read_cb, report);
-}
-
-static void external_report_reference_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data);
-
-static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Discover external descriptors failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       for ( ; descs; descs = descs->next) {
-               struct gatt_desc *desc = descs->data;
-
-               read_char(hog, hog->attrib, desc->handle,
-                                               external_report_reference_cb,
-                                               hog);
-       }
-}
-
-static void discover_external(struct bt_hog *hog, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                               gpointer user_data)
-{
-       bt_uuid_t uuid;
-
-       if (start > end)
-               return;
-
-       bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE);
-
-       discover_desc(hog, attrib, start, end, discover_external_cb,
-                                                               user_data);
-}
-
-static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct report *report = req->user_data;
-       struct bt_hog *hog = report->hog;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Discover report descriptors failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       for ( ; descs; descs = descs->next) {
-               struct gatt_desc *desc = descs->data;
-
-               switch (desc->uuid16) {
-               case GATT_CLIENT_CHARAC_CFG_UUID:
-                       report->ccc_handle = desc->handle;
-                       break;
-               case GATT_REPORT_REFERENCE:
-                       read_char(hog, hog->attrib, desc->handle,
-                                               report_reference_cb, report);
-                       break;
-               }
-       }
-}
-
-static void discover_report(struct bt_hog *hog, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                                       gpointer user_data)
-{
-       if (start > end)
-               return;
-
-       discover_desc(hog, attrib, start, end, discover_report_cb, user_data);
-}
-
-static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct report *report = req->user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Error reading Report value: %s", att_ecode2str(status));
-               return;
-       }
-
-       if (report->value)
-               g_free(report->value);
-
-       report->value = g_memdup(pdu, len);
-       report->len = len;
-}
-
-static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
-{
-       struct report *report;
-
-       report = g_new0(struct report, 1);
-       report->hog = hog;
-       report->decl = g_memdup(chr, sizeof(*chr));
-       hog->reports = g_slist_append(hog->reports, report);
-
-       read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
-
-       return report;
-}
-
-static void external_service_char_cb(uint8_t status, GSList *chars,
-                                                               void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       struct gatt_primary *primary = hog->primary;
-       struct report *report;
-       GSList *l;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               const char *str = att_ecode2str(status);
-               DBG("Discover external service characteristic failed: %s", str);
-               return;
-       }
-
-       for (l = chars; l; l = g_slist_next(l)) {
-               struct gatt_char *chr, *next;
-               uint16_t start, end;
-
-               chr = l->data;
-               next = l->next ? l->next->data : NULL;
-
-               DBG("0x%04x UUID: %s properties: %02x",
-                               chr->handle, chr->uuid, chr->properties);
-
-               report = report_new(hog, chr);
-               start = chr->value_handle + 1;
-               end = (next ? next->handle - 1 : primary->range.end);
-               discover_report(hog, hog->attrib, start, end, report);
-       }
-}
-
-static void external_report_reference_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       uint16_t uuid16;
-       bt_uuid_t uuid;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Read External Report Reference descriptor failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       if (plen != 3) {
-               error("Malformed ATT read response");
-               return;
-       }
-
-       uuid16 = get_le16(&pdu[1]);
-       DBG("External report reference read, external report characteristic "
-                                               "UUID: 0x%04x", uuid16);
-
-       /* Do not discover if is not a Report */
-       if (uuid16 != HOG_REPORT_UUID)
-               return;
-
-       bt_uuid16_create(&uuid, uuid16);
-       discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid,
-                                       external_service_char_cb, hog);
-}
-
-static int report_cmp(gconstpointer a, gconstpointer b)
-{
-       const struct report *ra = a, *rb = b;
-
-       /* sort by type first.. */
-       if (ra->type != rb->type)
-               return ra->type - rb->type;
-
-       /* skip id check in case of report id 0 */
-       if (!rb->id)
-               return 0;
-
-       /* ..then by id */
-       return ra->id - rb->id;
-}
-
-static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
-{
-       struct report cmp;
-       GSList *l;
-
-       cmp.type = type;
-       cmp.id = hog->has_report_id ? id : 0;
-
-       l = g_slist_find_custom(hog->reports, &cmp, report_cmp);
-
-       return l ? l->data : NULL;
-}
-
-static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype,
-                                                               uint8_t id)
-{
-       uint8_t type;
-
-       switch (rtype) {
-       case UHID_FEATURE_REPORT:
-               type = HOG_REPORT_TYPE_FEATURE;
-               break;
-       case UHID_OUTPUT_REPORT:
-               type = HOG_REPORT_TYPE_OUTPUT;
-               break;
-       case UHID_INPUT_REPORT:
-               type = HOG_REPORT_TYPE_INPUT;
-               break;
-       default:
-               return NULL;
-       }
-
-       return find_report(hog, type, id);
-}
-
-static void output_written_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Write output report failed: %s", att_ecode2str(status));
-               return;
-       }
-}
-
-static void forward_report(struct uhid_event *ev, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct report *report;
-       void *data;
-       int size;
-
-       report = find_report_by_rtype(hog, ev->u.output.rtype,
-                                                       ev->u.output.data[0]);
-       if (!report)
-               return;
-
-       data = ev->u.output.data;
-       size = ev->u.output.size;
-       if (hog->has_report_id && size > 0) {
-               data++;
-               --size;
-       }
-
-       DBG("Sending report type %d ID %d to handle 0x%X", report->type,
-                               report->id, report->decl->value_handle);
-
-       if (hog->attrib == NULL)
-               return;
-
-       if (report->decl->properties & GATT_CHR_PROP_WRITE)
-               write_char(hog, hog->attrib, report->decl->value_handle,
-                               data, size, output_written_cb, hog);
-       else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
-               gatt_write_cmd(hog->attrib, report->decl->value_handle,
-                                               data, size, NULL, NULL);
-}
-
-static void get_feature(struct uhid_event *ev, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct report *report;
-       struct uhid_event rsp;
-       int err;
-
-       memset(&rsp, 0, sizeof(rsp));
-       rsp.type = UHID_FEATURE_ANSWER;
-       rsp.u.feature_answer.id = ev->u.feature.id;
-
-       report = find_report_by_rtype(hog, ev->u.feature.rtype,
-                                                       ev->u.feature.rnum);
-       if (!report) {
-               rsp.u.feature_answer.err = ENOTSUP;
-               goto done;
-       }
-
-       if (!report->value) {
-               rsp.u.feature_answer.err = EIO;
-               goto done;
-       }
-
-       rsp.u.feature_answer.size = report->len;
-       memcpy(rsp.u.feature_answer.data, report->value, report->len);
-
-done:
-       err = bt_uhid_send(hog->uhid, &rsp);
-       if (err < 0)
-               error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void set_report_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct uhid_event rsp;
-       int err;
-
-       hog->setrep_att = 0;
-
-       memset(&rsp, 0, sizeof(rsp));
-       rsp.type = UHID_SET_REPORT_REPLY;
-       rsp.u.set_report_reply.id = hog->setrep_id;
-       rsp.u.set_report_reply.err = status;
-
-       if (status != 0)
-               error("Error setting Report value: %s", att_ecode2str(status));
-
-       err = bt_uhid_send(hog->uhid, &rsp);
-       if (err < 0)
-               error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void set_report(struct uhid_event *ev, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct report *report;
-       void *data;
-       int size;
-       int err;
-
-       /* uhid never sends reqs in parallel; if there's a req, it timed out */
-       if (hog->setrep_att) {
-               g_attrib_cancel(hog->attrib, hog->setrep_att);
-               hog->setrep_att = 0;
-       }
-
-       hog->setrep_id = ev->u.set_report.id;
-
-       report = find_report_by_rtype(hog, ev->u.set_report.rtype,
-                                                       ev->u.set_report.rnum);
-       if (!report) {
-               err = ENOTSUP;
-               goto fail;
-       }
-
-       data = ev->u.set_report.data;
-       size = ev->u.set_report.size;
-       if (hog->has_report_id && size > 0) {
-               data++;
-               --size;
-       }
-
-       DBG("Sending report type %d ID %d to handle 0x%X", report->type,
-                               report->id, report->decl->value_handle);
-
-       if (hog->attrib == NULL)
-               return;
-
-       hog->setrep_att = gatt_write_char(hog->attrib,
-                                               report->decl->value_handle,
-                                               data, size, set_report_cb,
-                                               hog);
-       if (!hog->setrep_att) {
-               err = ENOMEM;
-               goto fail;
-       }
-
-       return;
-fail:
-       /* cancel the request on failure */
-       set_report_cb(err, NULL, 0, hog);
-}
-
-static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct uhid_event rsp;
-       int err;
-
-       hog->getrep_att = 0;
-
-       memset(&rsp, 0, sizeof(rsp));
-       rsp.type = UHID_GET_REPORT_REPLY;
-       rsp.u.get_report_reply.id = hog->getrep_id;
-
-       if (status != 0) {
-               error("Error reading Report value: %s", att_ecode2str(status));
-               goto exit;
-       }
-
-       if (len == 0) {
-               error("Error reading Report, length %d", len);
-               status = EIO;
-               goto exit;
-       }
-
-       if (pdu[0] != 0x0b) {
-               error("Error reading Report, invalid response: %02x", pdu[0]);
-               status = EPROTO;
-               goto exit;
-       }
-
-       --len;
-       ++pdu;
-       if (hog->has_report_id && len > 0) {
-               --len;
-               ++pdu;
-       }
-
-       rsp.u.get_report_reply.size = len;
-       memcpy(rsp.u.get_report_reply.data, pdu, len);
-
-exit:
-       rsp.u.get_report_reply.err = status;
-       err = bt_uhid_send(hog->uhid, &rsp);
-       if (err < 0)
-               error("bt_uhid_send: %s", strerror(-err));
-}
-
-static void get_report(struct uhid_event *ev, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct report *report;
-       guint8 err;
-
-       /* uhid never sends reqs in parallel; if there's a req, it timed out */
-       if (hog->getrep_att) {
-               g_attrib_cancel(hog->attrib, hog->getrep_att);
-               hog->getrep_att = 0;
-       }
-
-       hog->getrep_id = ev->u.get_report.id;
-
-       report = find_report_by_rtype(hog, ev->u.get_report.rtype,
-                                                       ev->u.get_report.rnum);
-       if (!report) {
-               err = ENOTSUP;
-               goto fail;
-       }
-
-       hog->getrep_att = gatt_read_char(hog->attrib,
-                                               report->decl->value_handle,
-                                               get_report_cb, hog);
-       if (!hog->getrep_att) {
-               err = ENOMEM;
-               goto fail;
-       }
-
-       return;
-
-fail:
-       /* cancel the request on failure */
-       get_report_cb(err, NULL, 0, hog);
-}
-
-static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
-                                                               bool *is_long)
-{
-       if (!blen)
-               return false;
-
-       *is_long = (buf[0] == 0xfe);
-
-       if (*is_long) {
-               if (blen < 3)
-                       return false;
-
-               /*
-                * long item:
-                * byte 0 -> 0xFE
-                * byte 1 -> data size
-                * byte 2 -> tag
-                * + data
-                */
-
-               *len = buf[1] + 3;
-       } else {
-               uint8_t b_size;
-
-               /*
-                * short item:
-                * byte 0[1..0] -> data size (=0, 1, 2, 4)
-                * byte 0[3..2] -> type
-                * byte 0[7..4] -> tag
-                * + data
-                */
-
-               b_size = buf[0] & 0x03;
-               *len = (b_size ? 1 << (b_size - 1) : 0) + 1;
-       }
-
-       /* item length should be no more than input buffer length */
-       return *len <= blen;
-}
-
-static char *item2string(char *str, uint8_t *buf, uint8_t len)
-{
-       char *p = str;
-       int i;
-
-       /*
-        * Since long item tags are not defined except for vendor ones, we
-        * just ensure that short items are printed properly (up to 5 bytes).
-        */
-       for (i = 0; i < 6 && i < len; i++)
-               p += sprintf(p, " %02x", buf[i]);
-
-       /*
-        * If there are some data left, just add continuation mark to indicate
-        * this.
-        */
-       if (i < len)
-               sprintf(p, " ...");
-
-       return str;
-}
-
-static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
-       struct uhid_event ev;
-       ssize_t vlen;
-       char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
-       int i, err;
-       GError *gerr = NULL;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Report Map read failed: %s", att_ecode2str(status));
-               return;
-       }
-
-       vlen = dec_read_resp(pdu, plen, value, sizeof(value));
-       if (vlen < 0) {
-               error("ATT protocol error");
-               return;
-       }
-
-       DBG("Report MAP:");
-       for (i = 0; i < vlen;) {
-               ssize_t ilen = 0;
-               bool long_item = false;
-
-               if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
-                                                               &long_item)) {
-                       /* Report ID is short item with prefix 100001xx */
-                       if (!long_item && (value[i] & 0xfc) == 0x84)
-                               hog->has_report_id = TRUE;
-
-                       DBG("\t%s", item2string(itemstr, &value[i], ilen));
-
-                       i += ilen;
-               } else {
-                       error("Report Map parsing failed at %d", i);
-
-                       /* Just print remaining items at once and break */
-                       DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
-                       break;
-               }
-       }
-
-       /* create uHID device */
-       memset(&ev, 0, sizeof(ev));
-       ev.type = UHID_CREATE;
-
-       bt_io_get(g_attrib_get_channel(hog->attrib), &gerr,
-                       BT_IO_OPT_SOURCE, ev.u.create.phys,
-                       BT_IO_OPT_DEST, ev.u.create.uniq,
-                       BT_IO_OPT_INVALID);
-       if (gerr) {
-               error("Failed to connection details: %s", gerr->message);
-               g_error_free(gerr);
-               return;
-       }
-
-       strcpy((char *) ev.u.create.name, hog->name);
-       ev.u.create.vendor = hog->vendor;
-       ev.u.create.product = hog->product;
-       ev.u.create.version = hog->version;
-       ev.u.create.country = hog->bcountrycode;
-       ev.u.create.bus = BUS_BLUETOOTH;
-       ev.u.create.rd_data = value;
-       ev.u.create.rd_size = vlen;
-
-       err = bt_uhid_send(hog->uhid, &ev);
-       if (err < 0) {
-               error("bt_uhid_send: %s", strerror(-err));
-               return;
-       }
-
-       bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
-       bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
-       bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
-       bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
-}
-
-static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       uint8_t value[HID_INFO_SIZE];
-       ssize_t vlen;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("HID Information read failed: %s",
-                                               att_ecode2str(status));
-               return;
-       }
-
-       vlen = dec_read_resp(pdu, plen, value, sizeof(value));
-       if (vlen != 4) {
-               error("ATT protocol error");
-               return;
-       }
-
-       hog->bcdhid = get_le16(&value[0]);
-       hog->bcountrycode = value[2];
-       hog->flags = value[3];
-
-       DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
-                       hog->bcdhid, hog->bcountrycode, hog->flags);
-}
-
-static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
-                                                       gpointer user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       uint8_t value;
-       ssize_t vlen;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               error("Protocol Mode characteristic read failed: %s",
-                                                       att_ecode2str(status));
-               return;
-       }
-
-       vlen = dec_read_resp(pdu, plen, &value, sizeof(value));
-       if (vlen < 0) {
-               error("ATT protocol error");
-               return;
-       }
-
-       if (value == HOG_PROTO_MODE_BOOT) {
-               uint8_t nval = HOG_PROTO_MODE_REPORT;
-
-               DBG("HoG is operating in Boot Procotol Mode");
-
-               gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval,
-                                               sizeof(nval), NULL, NULL);
-       } else if (value == HOG_PROTO_MODE_REPORT)
-               DBG("HoG is operating in Report Protocol Mode");
-}
-
-static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       struct gatt_primary *primary = hog->primary;
-       bt_uuid_t report_uuid, report_map_uuid, info_uuid;
-       bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
-       struct report *report;
-       GSList *l;
-       uint16_t info_handle = 0, proto_mode_handle = 0;
-
-       destroy_gatt_req(req);
-
-       if (status != 0) {
-               const char *str = att_ecode2str(status);
-               DBG("Discover all characteristics failed: %s", str);
-               return;
-       }
-
-       bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
-       bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
-       bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
-       bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
-       bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
-
-       for (l = chars; l; l = g_slist_next(l)) {
-               struct gatt_char *chr, *next;
-               bt_uuid_t uuid;
-               uint16_t start, end;
-
-               chr = l->data;
-               next = l->next ? l->next->data : NULL;
-
-               DBG("0x%04x UUID: %s properties: %02x",
-                               chr->handle, chr->uuid, chr->properties);
-
-               bt_string_to_uuid(&uuid, chr->uuid);
-
-               start = chr->value_handle + 1;
-               end = (next ? next->handle - 1 : primary->range.end);
-
-               if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
-                       report = report_new(hog, chr);
-                       discover_report(hog, hog->attrib, start, end, report);
-               } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
-                       read_char(hog, hog->attrib, chr->value_handle,
-                                               report_map_read_cb, hog);
-                       discover_external(hog, hog->attrib, start, end, hog);
-               } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
-                       info_handle = chr->value_handle;
-               else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
-                       proto_mode_handle = chr->value_handle;
-               else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0)
-                       hog->ctrlpt_handle = chr->value_handle;
-       }
-
-       if (proto_mode_handle) {
-               hog->proto_mode_handle = proto_mode_handle;
-               read_char(hog, hog->attrib, proto_mode_handle,
-                                               proto_mode_read_cb, hog);
-       }
-
-       if (info_handle)
-               read_char(hog, hog->attrib, info_handle, info_read_cb, hog);
-}
-
-static void report_free(void *data)
-{
-       struct report *report = data;
-
-       g_free(report->value);
-       g_free(report->decl);
-       g_free(report);
-}
-
-static void cancel_gatt_req(struct gatt_request *req)
-{
-       if (g_attrib_cancel(req->hog->attrib, req->id))
-               destroy_gatt_req(req);
-}
-
-static void hog_free(void *data)
-{
-       struct bt_hog *hog = data;
-
-       bt_hog_detach(hog);
-
-       queue_destroy(hog->bas, (void *) bt_bas_unref);
-       g_slist_free_full(hog->instances, hog_free);
-
-       bt_scpp_unref(hog->scpp);
-       bt_dis_unref(hog->dis);
-       bt_uhid_unref(hog->uhid);
-       g_slist_free_full(hog->reports, report_free);
-       g_free(hog->name);
-       g_free(hog->primary);
-       queue_destroy(hog->gatt_op, (void *) destroy_gatt_req);
-       g_free(hog);
-}
-
-struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
-                                       uint16_t product, uint16_t version,
-                                       void *primary)
-{
-       return bt_hog_new(-1, name, vendor, product, version, primary);
-}
-
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
-                                       uint16_t product, uint16_t version,
-                                       void *primary)
-{
-       struct bt_hog *hog;
-
-       hog = g_try_new0(struct bt_hog, 1);
-       if (!hog)
-               return NULL;
-
-       hog->gatt_op = queue_new();
-       hog->bas = queue_new();
-
-       if (fd < 0)
-               hog->uhid = bt_uhid_new_default();
-       else
-               hog->uhid = bt_uhid_new(fd);
-
-       hog->uhid_fd = fd;
-
-       if (!hog->gatt_op || !hog->bas || !hog->uhid) {
-               hog_free(hog);
-               return NULL;
-       }
-
-       hog->name = g_strdup(name);
-       hog->vendor = vendor;
-       hog->product = product;
-       hog->version = version;
-
-       if (primary)
-               hog->primary = g_memdup(primary, sizeof(*hog->primary));
-
-       return bt_hog_ref(hog);
-}
-
-struct bt_hog *bt_hog_ref(struct bt_hog *hog)
-{
-       if (!hog)
-               return NULL;
-
-       __sync_fetch_and_add(&hog->ref_count, 1);
-
-       return hog;
-}
-
-void bt_hog_unref(struct bt_hog *hog)
-{
-       if (!hog)
-               return;
-
-       if (__sync_sub_and_fetch(&hog->ref_count, 1))
-               return;
-
-       hog_free(hog);
-}
-
-static void find_included_cb(uint8_t status, GSList *services, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       GSList *l;
-
-       DBG("");
-
-       destroy_gatt_req(req);
-
-       if (status) {
-               const char *str = att_ecode2str(status);
-               DBG("Find included failed: %s", str);
-               return;
-       }
-
-       for (l = services; l; l = l->next) {
-               struct gatt_included *include = l->data;
-
-               DBG("included: handle %x, uuid %s",
-                       include->handle, include->uuid);
-       }
-}
-
-static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
-{
-       if (hog->scpp) {
-               bt_scpp_attach(hog->scpp, hog->attrib);
-               return;
-       }
-
-       hog->scpp = bt_scpp_new(primary);
-       if (hog->scpp)
-               bt_scpp_attach(hog->scpp, hog->attrib);
-}
-
-static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
-                                       uint16_t version, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-
-       hog->vendor = vendor;
-       hog->product = product;
-       hog->version = version;
-}
-
-static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
-{
-       if (hog->dis) {
-               bt_dis_attach(hog->dis, hog->attrib);
-               return;
-       }
-
-       hog->dis = bt_dis_new(primary);
-       if (hog->dis) {
-               bt_dis_set_notification(hog->dis, dis_notify, hog);
-               bt_dis_attach(hog->dis, hog->attrib);
-       }
-}
-
-static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary)
-{
-       struct bt_bas *instance;
-
-       instance = bt_bas_new(primary);
-       if (!instance)
-               return;
-
-       bt_bas_attach(instance, hog->attrib);
-       queue_push_head(hog->bas, instance);
-}
-
-static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
-{
-       struct bt_hog *instance;
-
-       if (!hog->primary) {
-               hog->primary = g_memdup(primary, sizeof(*primary));
-               discover_char(hog, hog->attrib, primary->range.start,
-                                               primary->range.end, NULL,
-                                               char_discovered_cb, hog);
-               find_included(hog, hog->attrib, primary->range.start,
-                               primary->range.end, find_included_cb, hog);
-               return;
-       }
-
-       instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
-                                       hog->product, hog->version, primary);
-       if (!instance)
-               return;
-
-       find_included(instance, hog->attrib, primary->range.start,
-                       primary->range.end, find_included_cb, instance);
-
-       bt_hog_attach(instance, hog->attrib);
-       hog->instances = g_slist_append(hog->instances, instance);
-}
-
-static void primary_cb(uint8_t status, GSList *services, void *user_data)
-{
-       struct gatt_request *req = user_data;
-       struct bt_hog *hog = req->user_data;
-       struct gatt_primary *primary;
-       GSList *l;
-
-       DBG("");
-
-       destroy_gatt_req(req);
-
-       if (status) {
-               const char *str = att_ecode2str(status);
-               DBG("Discover primary failed: %s", str);
-               return;
-       }
-
-       if (!services) {
-               DBG("No primary service found");
-               return;
-       }
-
-       for (l = services; l; l = l->next) {
-               primary = l->data;
-
-               if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) {
-                       hog_attach_scpp(hog, primary);
-                       continue;
-               }
-
-               if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) {
-                       hog_attach_dis(hog, primary);
-                       continue;
-               }
-
-               if (strcmp(primary->uuid, BATTERY_UUID) == 0) {
-                       hog_attach_bas(hog, primary);
-                       continue;
-               }
-
-               if (strcmp(primary->uuid, HOG_UUID) == 0)
-                       hog_attach_hog(hog, primary);
-       }
-}
-
-bool bt_hog_attach(struct bt_hog *hog, void *gatt)
-{
-       struct gatt_primary *primary = hog->primary;
-       GSList *l;
-
-       if (hog->attrib)
-               return false;
-
-       hog->attrib = g_attrib_ref(gatt);
-
-       if (!primary) {
-               discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
-               return true;
-       }
-
-       if (hog->scpp)
-               bt_scpp_attach(hog->scpp, gatt);
-
-       if (hog->dis)
-               bt_dis_attach(hog->dis, gatt);
-
-       queue_foreach(hog->bas, (void *) bt_bas_attach, gatt);
-
-       for (l = hog->instances; l; l = l->next) {
-               struct bt_hog *instance = l->data;
-
-               bt_hog_attach(instance, gatt);
-       }
-
-       if (hog->reports == NULL) {
-               discover_char(hog, hog->attrib, primary->range.start,
-                                               primary->range.end, NULL,
-                                               char_discovered_cb, hog);
-               return true;
-       }
-
-       for (l = hog->reports; l; l = l->next) {
-               struct report *r = l->data;
-
-               r->notifyid = g_attrib_register(hog->attrib,
-                                       ATT_OP_HANDLE_NOTIFY,
-                                       r->decl->value_handle,
-                                       report_value_cb, r, NULL);
-       }
-
-       return true;
-}
-
-void bt_hog_detach(struct bt_hog *hog)
-{
-       GSList *l;
-
-       if (!hog->attrib)
-               return;
-
-       queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
-
-       for (l = hog->instances; l; l = l->next) {
-               struct bt_hog *instance = l->data;
-
-               bt_hog_detach(instance);
-       }
-
-       for (l = hog->reports; l; l = l->next) {
-               struct report *r = l->data;
-
-               if (r->notifyid > 0) {
-                       g_attrib_unregister(hog->attrib, r->notifyid);
-                       r->notifyid = 0;
-               }
-       }
-
-       if (hog->scpp)
-               bt_scpp_detach(hog->scpp);
-
-       if (hog->dis)
-               bt_dis_detach(hog->dis);
-
-       queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
-       g_attrib_unref(hog->attrib);
-       hog->attrib = NULL;
-}
-
-int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
-{
-       uint8_t value = suspend ? 0x00 : 0x01;
-
-       if (hog->attrib == NULL)
-               return -ENOTCONN;
-
-       if (hog->ctrlpt_handle == 0)
-               return -ENOTSUP;
-
-       gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value,
-                                       sizeof(value), NULL, NULL);
-
-       return 0;
-}
-
-int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
-{
-       struct report *report;
-       GSList *l;
-
-       if (!hog)
-               return -EINVAL;
-
-       if (!hog->attrib)
-               return -ENOTCONN;
-
-       report = find_report(hog, type, 0);
-       if (!report)
-               return -ENOTSUP;
-
-       DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
-
-       if (report->decl->properties & GATT_CHR_PROP_WRITE)
-               write_char(hog, hog->attrib, report->decl->value_handle,
-                               data, size, output_written_cb, hog);
-
-       if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
-               gatt_write_cmd(hog->attrib, report->decl->value_handle,
-                                               data, size, NULL, NULL);
-
-       for (l = hog->instances; l; l = l->next) {
-               struct bt_hog *instance = l->data;
-
-               bt_hog_send_report(instance, data, size, type);
-       }
-
-       return 0;
-}
diff --git a/android/scpp.c b/android/scpp.c
deleted file mode 100755 (executable)
index f8f81f3..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2012  Nordic Semiconductor Inc.
- *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdbool.h>
-#include <errno.h>
-
-#include <glib.h>
-
-#include "src/log.h"
-
-#include "lib/bluetooth.h"
-#include "lib/sdp.h"
-#include "lib/uuid.h"
-
-#include "src/shared/util.h"
-#include "src/shared/queue.h"
-
-#include "attrib/att.h"
-#include "attrib/gattrib.h"
-#include "attrib/gatt.h"
-
-#include "android/scpp.h"
-
-#define SCAN_INTERVAL_WIN_UUID         0x2A4F
-#define SCAN_REFRESH_UUID              0x2A31
-
-#define SCAN_INTERVAL          0x0060
-#define SCAN_WINDOW            0x0030
-#define SERVER_REQUIRES_REFRESH        0x00
-
-struct bt_scpp {
-       int ref_count;
-       GAttrib *attrib;
-       struct gatt_primary *primary;
-       uint16_t interval;
-       uint16_t window;
-       uint16_t iwhandle;
-       uint16_t refresh_handle;
-       guint refresh_cb_id;
-       struct queue *gatt_op;
-};
-
-static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
-                                               uint16_t start, uint16_t end,
-                                               bt_uuid_t *uuid, gatt_cb_t func,
-                                               gpointer user_data)
-{
-       unsigned int id;
-
-       id = gatt_discover_char(attrib, start, end, uuid, func, user_data);
-
-       if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
-               return;
-
-       error("scpp: Could not discover characteristic");
-       g_attrib_cancel(attrib, id);
-}
-
-static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
-                               uint16_t start, uint16_t end, bt_uuid_t *uuid,
-                               gatt_cb_t func, gpointer user_data)
-{
-       unsigned int id;
-
-       id = gatt_discover_desc(attrib, start, end, uuid, func, user_data);
-
-       if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
-               return;
-
-       error("scpp: Could not discover descriptor");
-       g_attrib_cancel(attrib, id);
-}
-
-static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
-                                       const uint8_t *value, size_t vlen,
-                                       GAttribResultFunc func,
-                                       gpointer user_data)
-{
-       unsigned int id;
-
-       id = gatt_write_char(attrib, handle, value, vlen, func, user_data);
-
-       if (queue_push_head(scan->gatt_op, UINT_TO_PTR(id)))
-               return;
-
-       error("scpp: Could not read char");
-       g_attrib_cancel(attrib, id);
-}
-
-static void scpp_free(struct bt_scpp *scan)
-{
-       bt_scpp_detach(scan);
-
-       g_free(scan->primary);
-       queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */
-       g_free(scan);
-}
-
-struct bt_scpp *bt_scpp_new(void *primary)
-{
-       struct bt_scpp *scan;
-
-       scan = g_try_new0(struct bt_scpp, 1);
-       if (!scan)
-               return NULL;
-
-       scan->interval = SCAN_INTERVAL;
-       scan->window = SCAN_WINDOW;
-
-       scan->gatt_op = queue_new();
-       if (!scan->gatt_op) {
-               scpp_free(scan);
-               return NULL;
-       }
-
-       if (primary)
-               scan->primary = g_memdup(primary, sizeof(*scan->primary));
-
-       return bt_scpp_ref(scan);
-}
-
-struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan)
-{
-       if (!scan)
-               return NULL;
-
-       __sync_fetch_and_add(&scan->ref_count, 1);
-
-       return scan;
-}
-
-void bt_scpp_unref(struct bt_scpp *scan)
-{
-       if (!scan)
-               return;
-
-       if (__sync_sub_and_fetch(&scan->ref_count, 1))
-               return;
-
-       scpp_free(scan);
-}
-
-static void write_scan_params(GAttrib *attrib, uint16_t handle,
-                                       uint16_t interval, uint16_t window)
-{
-       uint8_t value[4];
-
-       put_le16(interval, &value[0]);
-       put_le16(window, &value[2]);
-
-       gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
-}
-
-static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
-                                               gpointer user_data)
-{
-       struct bt_scpp *scan = user_data;
-
-       DBG("Server requires refresh: %d", pdu[3]);
-
-       if (pdu[3] == SERVER_REQUIRES_REFRESH)
-               write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
-                                                               scan->window);
-}
-
-static void ccc_written_cb(guint8 status, const guint8 *pdu,
-                                       guint16 plen, gpointer user_data)
-{
-       struct bt_scpp *scan = user_data;
-
-       if (status != 0) {
-               error("Write Scan Refresh CCC failed: %s",
-                                               att_ecode2str(status));
-               return;
-       }
-
-       DBG("Scan Refresh: notification enabled");
-
-       scan->refresh_cb_id = g_attrib_register(scan->attrib,
-                               ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
-                               refresh_value_cb, scan, NULL);
-}
-
-static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
-                                                               void *user_data)
-{
-       uint8_t value[2];
-
-       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-
-       write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
-                                                               user_data);
-}
-
-static void discover_descriptor_cb(uint8_t status, GSList *descs,
-                                                               void *user_data)
-{
-       struct bt_scpp *scan = user_data;
-       struct gatt_desc *desc;
-
-       if (status != 0) {
-               error("Discover descriptors failed: %s", att_ecode2str(status));
-               return;
-       }
-
-       /* There will be only one descriptor on list and it will be CCC */
-       desc = descs->data;
-
-       write_ccc(scan, scan->attrib, desc->handle, scan);
-}
-
-static void refresh_discovered_cb(uint8_t status, GSList *chars,
-                                                               void *user_data)
-{
-       struct bt_scpp *scan = user_data;
-       struct gatt_char *chr;
-       uint16_t start, end;
-       bt_uuid_t uuid;
-
-       if (status) {
-               error("Scan Refresh %s", att_ecode2str(status));
-               return;
-       }
-
-       if (!chars) {
-               DBG("Scan Refresh not supported");
-               return;
-       }
-
-       chr = chars->data;
-
-       DBG("Scan Refresh handle: 0x%04x", chr->value_handle);
-
-       start = chr->value_handle + 1;
-       end = scan->primary->range.end;
-
-       if (start > end)
-               return;
-
-       scan->refresh_handle = chr->value_handle;
-
-       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
-
-       discover_desc(scan, scan->attrib, start, end, &uuid,
-                                       discover_descriptor_cb, user_data);
-}
-
-static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
-{
-       struct bt_scpp *scan = user_data;
-       struct gatt_char *chr;
-
-       if (status) {
-               error("Discover Scan Interval Window: %s",
-                                               att_ecode2str(status));
-               return;
-       }
-
-       chr = chars->data;
-       scan->iwhandle = chr->value_handle;
-
-       DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
-
-       write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
-                                                               scan->window);
-}
-
-bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
-{
-       bt_uuid_t iwin_uuid, refresh_uuid;
-
-       if (!scan || scan->attrib || !scan->primary)
-               return false;
-
-       scan->attrib = g_attrib_ref(attrib);
-
-       if (scan->iwhandle)
-               write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
-                                                               scan->window);
-       else {
-               bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
-               discover_char(scan, scan->attrib, scan->primary->range.start,
-                                       scan->primary->range.end, &iwin_uuid,
-                                       iwin_discovered_cb, scan);
-       }
-
-       if (scan->refresh_handle)
-               scan->refresh_cb_id = g_attrib_register(scan->attrib,
-                               ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
-                               refresh_value_cb, scan, NULL);
-       else {
-               bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
-               discover_char(scan, scan->attrib, scan->primary->range.start,
-                                       scan->primary->range.end, &refresh_uuid,
-                                       refresh_discovered_cb, scan);
-       }
-
-       return true;
-}
-
-static void cancel_gatt_req(void *data, void *user_data)
-{
-       unsigned int id = PTR_TO_UINT(data);
-       struct bt_scpp *scan = user_data;
-
-       g_attrib_cancel(scan->attrib, id);
-}
-
-void bt_scpp_detach(struct bt_scpp *scan)
-{
-       if (!scan || !scan->attrib)
-               return;
-
-       if (scan->refresh_cb_id > 0) {
-               g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
-               scan->refresh_cb_id = 0;
-       }
-
-       queue_foreach(scan->gatt_op, cancel_gatt_req, scan);
-       g_attrib_unref(scan->attrib);
-       scan->attrib = NULL;
-}
-
-bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value)
-{
-       if (!scan)
-               return false;
-
-       /* TODO: Check valid range */
-
-       scan->interval = value;
-
-       return true;
-}
-
-bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value)
-{
-       if (!scan)
-               return false;
-
-       /* TODO: Check valid range */
-
-       scan->window = value;
-
-       return true;
-}
diff --git a/android/scpp.h b/android/scpp.h
deleted file mode 100755 (executable)
index 048fb9f..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2014  Intel Corporation. All rights reserved.
- *
- *
- *  This library is free software; you can rescpptribute 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 scpptributed 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
- *
- */
-
-struct bt_scpp;
-
-struct bt_scpp *bt_scpp_new(void *primary);
-
-struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan);
-void bt_scpp_unref(struct bt_scpp *scan);
-
-bool bt_scpp_attach(struct bt_scpp *scan, void *gatt);
-void bt_scpp_detach(struct bt_scpp *scan);
-
-bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value);
-bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value);
index 22077a0..b42a81d 100755 (executable)
@@ -231,7 +231,7 @@ static bt_property_t prop_emu_default_set[] = {
        { BT_PROPERTY_UUIDS, sizeof(emu_uuids_val), &emu_uuids_val },
 };
 
-static bt_property_t prop_emu_remote_bles_default_set[] = {
+static bt_property_t prop_emu_remote_ble_default_set[] = {
        { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
                                                &emu_remote_bdaddr_val },
        { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val),
@@ -240,7 +240,7 @@ static bt_property_t prop_emu_remote_bles_default_set[] = {
                                                &emu_remote_ble_rssi_val },
 };
 
-static bt_property_t prop_emu_remotes_default_set[] = {
+static bt_property_t prop_emu_remote_bredr_default_set[] = {
        { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
                                                &emu_remote_bdaddr_val },
        { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_type_val),
@@ -249,6 +249,11 @@ static bt_property_t prop_emu_remotes_default_set[] = {
                                                &emu_remote_rssi_val },
 };
 
+static bt_property_t prop_emu_remote_any_default_set[] = {
+       { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val),
+                                               &emu_remote_bdaddr_val },
+};
+
 static bt_property_t prop_emu_remote_bles_query_set[] = {
        { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val),
                                                &emu_remote_tod_ble_val },
@@ -644,11 +649,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Discovery Device Found",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remote_bles_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_ble_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -656,10 +661,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get Props - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -670,10 +676,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get BDNAME - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -684,10 +691,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get UUIDS - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -698,10 +706,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get COD - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -712,10 +721,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get TOD - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -726,10 +736,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get RSSI - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -740,10 +751,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get TIMESTAMP - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -754,10 +766,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get BDADDR - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -767,10 +780,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get SCAN_MODE - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -780,10 +794,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get BONDED_DEVICES - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -793,10 +808,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -806,10 +822,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get VERSION_INFO - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -819,10 +836,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Get FRIENDLY_NAME - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -832,10 +850,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set FRIENDLY_NAME - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -848,10 +867,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set BDNAME - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -861,10 +881,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set UUIDS - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -874,10 +895,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set COD - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -887,10 +909,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set TOD - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -900,10 +923,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set RSSI - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -913,10 +937,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set TIMESTAMP - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -926,10 +951,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set BDADDR - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -939,10 +965,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set SERVICE_RECORD - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -952,10 +979,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set SCAN_MODE - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -965,10 +993,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set BONDED_DEVICES - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -978,10 +1007,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_any_default_set, 1),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -991,12 +1021,12 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Create Bond PIN - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -1014,12 +1044,12 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Create Bond PIN - Bad PIN",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -1038,13 +1068,13 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Create Bond SSP -Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -1062,13 +1092,13 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Create Bond SSP - Negative reply",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -1120,13 +1150,13 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Cancel Bonding - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
@@ -1146,13 +1176,13 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDR("Bluetooth Remove Bond - Success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_io_cap, &display_yes_no_io_cap),
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
-               CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               CALLBACK_DEVICE_FOUND(prop_emu_remote_bredr_default_set, 3),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STOPPED),
index 88be3d8..6400a3f 100755 (executable)
@@ -1917,6 +1917,20 @@ static void init_notify_params_action(void)
        schedule_action_verification(step);
 }
 
+static void trigger_device_found(void *user_data)
+{
+       emu_setup_powered_remote_action();
+}
+
+static void delayemu_setup_powered_remote_action(void)
+{
+       /* Make sure discovery is enabled before enabling advertising.
+        * Unfortunately GATT HAL doesn't have discovering callback like
+        * Bluetooth HAL so we need to delay
+        */
+       tester_wait(1, trigger_device_found, NULL);
+}
+
 static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Init",
                ACTION_SUCCESS(dummy_action, NULL),
@@ -1936,11 +1950,11 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - Scan",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
@@ -1950,12 +1964,12 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - LE Connect",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
@@ -1969,12 +1983,12 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - LE Disconnect",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
@@ -1993,7 +2007,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - LE Multiple Client Conn./Disc.",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
@@ -2001,6 +2014,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(gatt_client_register_action, &app2_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2027,7 +2041,7 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - Listen and Disconnect",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(bt_set_property_action,
@@ -2055,7 +2069,7 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Client - Double Listen",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(bt_set_property_action,
@@ -2103,12 +2117,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, search_service),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2125,12 +2139,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, search_service_2),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2148,12 +2162,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, search_service_3),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2169,12 +2183,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, search_service_4),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2190,12 +2204,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_characteristic_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2215,12 +2229,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_characteristic_2),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2240,12 +2254,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_characteristic_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2266,12 +2280,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_descriptor_0),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2295,12 +2309,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_descriptor_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2324,12 +2338,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_descriptor_2),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2360,12 +2374,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_descriptor_3),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2389,12 +2403,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_included_0),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2414,12 +2428,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_included_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2439,12 +2453,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_included_2),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2464,12 +2478,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, get_included_3),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2490,12 +2504,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_read_params_action, &set_read_param_1),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2521,12 +2535,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_read_params_action, &set_read_param_2),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2551,12 +2565,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_read_params_action, &set_read_param_3),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2581,12 +2595,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_read_params_action, &set_read_param_4),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2615,12 +2629,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_read_params_action, &set_read_param_5),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2649,12 +2663,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_read_params_action, &set_read_param_6),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2683,12 +2697,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_1),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2713,12 +2727,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_1),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2743,12 +2757,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_2),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2773,12 +2787,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_3),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_FAIL(gatt_client_write_characteristic_action,
@@ -2792,12 +2806,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, notification_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2822,12 +2836,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, notification_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2858,12 +2872,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_notify_params_action, &set_notify_param_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2891,12 +2905,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_notify_params_action, &set_notify_param_2),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2924,12 +2938,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_4),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2958,12 +2972,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_6),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -2992,12 +3006,12 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
                ACTION_SUCCESS(init_write_params_action, &set_write_param_5),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
                CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
                ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+               ACTION_SUCCESS(delayemu_setup_powered_remote_action, NULL),
                CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
                ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
                ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
@@ -3036,7 +3050,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - LE Connect",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3044,6 +3057,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3056,7 +3070,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - LE Disconnect",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3064,6 +3077,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3081,7 +3095,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - LE Multiple Server Conn./Disc",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3091,6 +3104,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3380,7 +3394,6 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, send_indication_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3388,6 +3401,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3405,7 +3419,6 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(init_pdus, send_notification_1),
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3413,6 +3426,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3429,7 +3443,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Send Notification, wrong conn id",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3437,6 +3450,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3451,7 +3465,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Send response to read char request",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3473,6 +3486,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3494,7 +3508,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Send response to write char request",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3516,6 +3529,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3541,7 +3555,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Find By Type - Attribute not found",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3563,6 +3576,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3582,7 +3596,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Srvc change write req. success",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3590,6 +3603,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
@@ -3614,7 +3628,6 @@ static struct test_case test_cases[] = {
        TEST_CASE_BREDRLE("Gatt Server - Send error resp to write char request",
                ACTION_SUCCESS(bluetooth_enable_action, NULL),
                CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
-               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
                ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
                ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
@@ -3636,6 +3649,7 @@ static struct test_case test_cases[] = {
                ACTION_SUCCESS(bt_start_discovery_action, NULL),
                CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
                                                        BT_DISCOVERY_STARTED),
+               ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
                CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
                ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
                ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
index 3a55792..e9b1353 100755 (executable)
@@ -2722,12 +2722,11 @@ void emu_setup_powered_remote_action(void)
 
        if ((data->hciemu_type == HCIEMU_TYPE_LE) ||
                                (data->hciemu_type == HCIEMU_TYPE_BREDRLE)) {
-               uint8_t adv[4];
+               uint8_t adv[3];
 
                adv[0] = 0x02;  /* Field length */
                adv[1] = 0x01;  /* Flags */
                adv[2] = 0x02;  /* Flags value */
-               adv[3] = 0x00;  /* Field terminator */
 
                bthost_set_adv_data(bthost, adv, sizeof(adv));
                bthost_set_adv_enable(bthost, 0x01);
index ffc16ac..658eef2 100755 (executable)
@@ -11,12 +11,17 @@ fi
                --mandir=/usr/share/man \
                --sysconfdir=/etc \
                --localstatedir=/var \
+               --enable-tools \
                --enable-manpages \
                --enable-backtrace \
+               --enable-testing \
                --enable-experimental \
+               --enable-deprecated \
                --enable-nfc \
                --enable-sap \
                --enable-health \
                --enable-android \
                --enable-sixaxis \
+               --enable-midi \
+               --enable-mesh \
                --disable-datafiles $*
index 62201d5..f0fd359 100755 (executable)
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
-#include <readline/readline.h>
-#include <wordexp.h>
+#include <stdbool.h>
+#include <string.h>
 
 #include "gdbus/gdbus.h"
-#include "display.h"
+#include "src/shared/shell.h"
 #include "advertising.h"
 
 #define AD_PATH "/org/bluez/advertising"
 #define AD_IFACE "org.bluez.LEAdvertisement1"
 
-static gboolean registered = FALSE;
-static char *ad_type = NULL;
-static char **ad_uuids = NULL;
-static size_t ad_uuids_len = 0;
-static char *ad_service_uuid = NULL;
-static uint8_t ad_service_data[25];
-static uint8_t ad_service_data_len = 0;
-static uint16_t ad_manufacturer_id;
-static uint8_t ad_manufacturer_data[25];
-static uint8_t ad_manufacturer_data_len = 0;
-static gboolean ad_tx_power = FALSE;
+struct ad_data {
+       uint8_t data[25];
+       uint8_t len;
+};
+
+struct service_data {
+       char *uuid;
+       struct ad_data data;
+};
+
+struct manufacturer_data {
+       uint16_t id;
+       struct ad_data data;
+};
+
+static struct ad {
+       bool registered;
+       char *type;
+       char *local_name;
+       uint16_t local_appearance;
+       uint16_t duration;
+       uint16_t timeout;
+       char **uuids;
+       size_t uuids_len;
+       struct service_data service;
+       struct manufacturer_data manufacturer;
+       bool tx_power;
+       bool name;
+       bool appearance;
+} ad = {
+       .local_appearance = UINT16_MAX,
+};
 
 static void ad_release(DBusConnection *conn)
 {
-       registered = FALSE;
+       ad.registered = false;
 
        g_dbus_unregister_interface(conn, AD_PATH, AD_IFACE);
 }
@@ -60,7 +81,7 @@ static void ad_release(DBusConnection *conn)
 static DBusMessage *release_advertising(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
-       rl_printf("Advertising released\n");
+       bt_shell_printf("Advertising released\n");
 
        ad_release(conn);
 
@@ -94,15 +115,15 @@ static void register_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == FALSE) {
-               registered = TRUE;
-               rl_printf("Advertising object registered\n");
+               ad.registered = true;
+               bt_shell_printf("Advertising object registered\n");
        } else {
-               rl_printf("Failed to register advertisement: %s\n", error.name);
+               bt_shell_printf("Failed to register advertisement: %s\n", error.name);
                dbus_error_free(&error);
 
                if (g_dbus_unregister_interface(conn, AD_PATH,
                                                AD_IFACE) == FALSE)
-                       rl_printf("Failed to unregister advertising object\n");
+                       bt_shell_printf("Failed to unregister advertising object\n");
        }
 }
 
@@ -111,8 +132,8 @@ static gboolean get_type(const GDBusPropertyTable *property,
 {
        const char *type = "peripheral";
 
-       if (!ad_type || strlen(ad_type) > 0)
-               type = ad_type;
+       if (ad.type && strlen(ad.type) > 0)
+               type = ad.type;
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);
 
@@ -121,7 +142,7 @@ static gboolean get_type(const GDBusPropertyTable *property,
 
 static gboolean uuids_exists(const GDBusPropertyTable *property, void *data)
 {
-       return ad_uuids_len != 0;
+       return ad.uuids_len != 0;
 }
 
 static gboolean get_uuids(const GDBusPropertyTable *property,
@@ -132,9 +153,9 @@ static gboolean get_uuids(const GDBusPropertyTable *property,
 
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
 
-       for (i = 0; i < ad_uuids_len; i++)
+       for (i = 0; i < ad.uuids_len; i++)
                dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
-                                                       &ad_uuids[i]);
+                                                       &ad.uuids[i]);
 
        dbus_message_iter_close_container(iter, &array);
 
@@ -197,19 +218,20 @@ static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
 static gboolean service_data_exists(const GDBusPropertyTable *property,
                                                                void *data)
 {
-       return ad_service_uuid != NULL;
+       return ad.service.uuid != NULL;
 }
 
 static gboolean get_service_data(const GDBusPropertyTable *property,
                                DBusMessageIter *iter, void *user_data)
 {
        DBusMessageIter dict;
-       const uint8_t *data = ad_service_data;
+       struct ad_data *data = &ad.service.data;
+       uint8_t *val = data->data;
 
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
 
-       dict_append_array(&dict, ad_service_uuid, DBUS_TYPE_BYTE, &data,
-                                                       ad_service_data_len);
+       dict_append_array(&dict, ad.service.uuid, DBUS_TYPE_BYTE, &val,
+                                                               data->len);
 
        dbus_message_iter_close_container(iter, &dict);
 
@@ -219,35 +241,111 @@ static gboolean get_service_data(const GDBusPropertyTable *property,
 static gboolean manufacturer_data_exists(const GDBusPropertyTable *property,
                                                                void *data)
 {
-       return ad_manufacturer_id != 0;
+       return ad.manufacturer.id != 0;
 }
 
 static gboolean get_manufacturer_data(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *user_data)
 {
        DBusMessageIter dict;
-       const uint8_t *data = ad_manufacturer_data;
+       struct ad_data *data = &ad.manufacturer.data;
+       uint8_t *val = data->data;
 
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{qv}", &dict);
 
-       dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad_manufacturer_id,
-                                       DBUS_TYPE_BYTE, &data,
-                                       ad_manufacturer_data_len);
+       dict_append_basic_array(&dict, DBUS_TYPE_UINT16, &ad.manufacturer.id,
+                                       DBUS_TYPE_BYTE, &val, data->len);
 
        dbus_message_iter_close_container(iter, &dict);
 
        return TRUE;
 }
 
-static gboolean tx_power_exists(const GDBusPropertyTable *property, void *data)
+static gboolean includes_exists(const GDBusPropertyTable *property, void *data)
+{
+       return ad.tx_power || ad.name || ad.appearance;
+}
+
+static gboolean get_includes(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter, void *user_data)
+{
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "as", &array);
+
+       if (ad.tx_power) {
+               const char *str = "tx-power";
+
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+       }
+
+       if (ad.name) {
+               const char *str = "local-name";
+
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+       }
+
+       if (ad.appearance) {
+               const char *str = "appearance";
+
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
+       }
+
+       dbus_message_iter_close_container(iter, &array);
+
+
+       return TRUE;
+}
+
+static gboolean local_name_exits(const GDBusPropertyTable *property, void *data)
+{
+       return ad.local_name ? TRUE : FALSE;
+}
+
+static gboolean get_local_name(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter, void *user_data)
+{
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ad.local_name);
+
+       return TRUE;
+}
+
+static gboolean appearance_exits(const GDBusPropertyTable *property, void *data)
+{
+       return ad.local_appearance != UINT16_MAX ? TRUE : FALSE;
+}
+
+static gboolean get_appearance(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter, void *user_data)
+{
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+                                                       &ad.local_appearance);
+
+       return TRUE;
+}
+
+static gboolean duration_exits(const GDBusPropertyTable *property, void *data)
+{
+       return ad.duration;
+}
+
+static gboolean get_duration(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter, void *user_data)
+{
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.duration);
+
+       return TRUE;
+}
+
+static gboolean timeout_exits(const GDBusPropertyTable *property, void *data)
 {
-       return ad_tx_power;
+       return ad.timeout;
 }
 
-static gboolean get_tx_power(const GDBusPropertyTable *property,
+static gboolean get_timeout(const GDBusPropertyTable *property,
                                DBusMessageIter *iter, void *user_data)
 {
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &ad_tx_power);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &ad.timeout);
 
        return TRUE;
 }
@@ -258,29 +356,34 @@ static const GDBusPropertyTable ad_props[] = {
        { "ServiceData", "a{sv}", get_service_data, NULL, service_data_exists },
        { "ManufacturerData", "a{qv}", get_manufacturer_data, NULL,
                                                manufacturer_data_exists },
-       { "IncludeTxPower", "b", get_tx_power, NULL, tx_power_exists },
+       { "Includes", "as", get_includes, NULL, includes_exists },
+       { "LocalName", "s", get_local_name, NULL, local_name_exits },
+       { "Appearance", "q", get_appearance, NULL, appearance_exits },
+       { "Duration", "q", get_duration, NULL, duration_exits },
+       { "Timeout", "q", get_timeout, NULL, timeout_exits },
        { }
 };
 
 void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type)
 {
-       if (registered == TRUE) {
-               rl_printf("Advertisement is already registered\n");
+       if (ad.registered) {
+               bt_shell_printf("Advertisement is already registered\n");
                return;
        }
 
-       ad_type = g_strdup(type);
+       g_free(ad.type);
+       ad.type = g_strdup(type);
 
        if (g_dbus_register_interface(conn, AD_PATH, AD_IFACE, ad_methods,
                                        NULL, ad_props, NULL, NULL) == FALSE) {
-               rl_printf("Failed to register advertising object\n");
+               bt_shell_printf("Failed to register advertising object\n");
                return;
        }
 
        if (g_dbus_proxy_method_call(manager, "RegisterAdvertisement",
                                        register_setup, register_reply,
                                        conn, NULL) == FALSE) {
-               rl_printf("Failed to register advertising\n");
+               bt_shell_printf("Failed to register advertising\n");
                return;
        }
 }
@@ -300,13 +403,13 @@ static void unregister_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == FALSE) {
-               registered = FALSE;
-               rl_printf("Advertising object unregistered\n");
+               ad.registered = false;
+               bt_shell_printf("Advertising object unregistered\n");
                if (g_dbus_unregister_interface(conn, AD_PATH,
                                                        AD_IFACE) == FALSE)
-                       rl_printf("Failed to unregister advertising object\n");
+                       bt_shell_printf("Failed to unregister advertising object\n");
        } else {
-               rl_printf("Failed to unregister advertisement: %s\n",
+               bt_shell_printf("Failed to unregister advertisement: %s\n",
                                                                error.name);
                dbus_error_free(&error);
        }
@@ -317,136 +420,206 @@ void ad_unregister(DBusConnection *conn, GDBusProxy *manager)
        if (!manager)
                ad_release(conn);
 
+       if (!ad.registered)
+               return;
+
+       g_free(ad.type);
+       ad.type = NULL;
+
        if (g_dbus_proxy_method_call(manager, "UnregisterAdvertisement",
                                        unregister_setup, unregister_reply,
                                        conn, NULL) == FALSE) {
-               rl_printf("Failed to unregister advertisement method\n");
+               bt_shell_printf("Failed to unregister advertisement method\n");
                return;
        }
 }
 
-void ad_advertise_uuids(const char *arg)
+void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[])
 {
-       g_strfreev(ad_uuids);
-       ad_uuids = NULL;
-       ad_uuids_len = 0;
+       g_strfreev(ad.uuids);
+       ad.uuids = NULL;
+       ad.uuids_len = 0;
 
-       if (!arg || !strlen(arg))
+       if (argc < 2 || !strlen(argv[1]))
                return;
 
-       ad_uuids = g_strsplit(arg, " ", -1);
-       if (!ad_uuids) {
-               rl_printf("Failed to parse input\n");
+       ad.uuids = g_strdupv(&argv[1]);
+       if (!ad.uuids) {
+               bt_shell_printf("Failed to parse input\n");
                return;
        }
 
-       ad_uuids_len = g_strv_length(ad_uuids);
+       ad.uuids_len = g_strv_length(ad.uuids);
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceUUIDs");
 }
 
 static void ad_clear_service(void)
 {
-       g_free(ad_service_uuid);
-       ad_service_uuid = NULL;
-       memset(ad_service_data, 0, sizeof(ad_service_data));
-       ad_service_data_len = 0;
+       g_free(ad.service.uuid);
+       memset(&ad.service, 0, sizeof(ad.service));
 }
 
-void ad_advertise_service(const char *arg)
+void ad_advertise_service(DBusConnection *conn, int argc, char *argv[])
 {
-       wordexp_t w;
        unsigned int i;
-
-       if (wordexp(arg, &w, WRDE_NOCMD)) {
-               rl_printf("Invalid argument\n");
-               return;
-       }
+       struct ad_data *data;
 
        ad_clear_service();
 
-       if (w.we_wordc == 0)
-               goto done;
+       if (argc < 2)
+               return;
 
-       ad_service_uuid = g_strdup(w.we_wordv[0]);
+       ad.service.uuid = g_strdup(argv[1]);
+       data = &ad.service.data;
 
-       for (i = 1; i < w.we_wordc; i++) {
+       for (i = 1; i < (unsigned int) argc; i++) {
                long int val;
                char *endptr = NULL;
 
-               if (i >= G_N_ELEMENTS(ad_service_data)) {
-                       rl_printf("Too much data\n");
-                       goto done;
+               if (i >= G_N_ELEMENTS(data->data)) {
+                       bt_shell_printf("Too much data\n");
+                       ad_clear_service();
+                       return;
                }
 
-               val = strtol(w.we_wordv[i], &endptr, 0);
+               val = strtol(argv[i], &endptr, 0);
                if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
-                       rl_printf("Invalid value at index %d\n", i);
+                       bt_shell_printf("Invalid value at index %d\n", i);
                        ad_clear_service();
-                       goto done;
+                       return;
                }
 
-               ad_service_data[ad_service_data_len] = val;
-               ad_service_data_len++;
+               data->data[data->len] = val;
+               data->len++;
        }
 
-done:
-       wordfree(&w);
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "ServiceData");
 }
 
 static void ad_clear_manufacturer(void)
 {
-       ad_manufacturer_id = 0;
-       memset(ad_manufacturer_data, 0, sizeof(ad_manufacturer_data));
-       ad_manufacturer_data_len = 0;
+       memset(&ad.manufacturer, 0, sizeof(ad.manufacturer));
 }
 
-void ad_advertise_manufacturer(const char *arg)
+void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[])
 {
-       wordexp_t w;
        unsigned int i;
        char *endptr = NULL;
        long int val;
-
-       if (wordexp(arg, &w, WRDE_NOCMD)) {
-               rl_printf("Invalid argument\n");
-               return;
-       }
+       struct ad_data *data;
 
        ad_clear_manufacturer();
 
-       if (w.we_wordc == 0)
-               goto done;
+       if (argc < 2)
+               return;
 
-       val = strtol(w.we_wordv[0], &endptr, 0);
+       val = strtol(argv[1], &endptr, 0);
        if (!endptr || *endptr != '\0' || val > UINT16_MAX) {
-               rl_printf("Invalid manufacture id\n");
-               goto done;
+               bt_shell_printf("Invalid manufacture id\n");
+               return;
        }
 
-       ad_manufacturer_id = val;
+       ad.manufacturer.id = val;
+       data = &ad.manufacturer.data;
 
-       for (i = 1; i < w.we_wordc; i++) {
-               if (i >= G_N_ELEMENTS(ad_service_data)) {
-                       rl_printf("Too much data\n");
-                       goto done;
+       for (i = 2; i < (unsigned int) argc; i++) {
+               if (i >= G_N_ELEMENTS(data->data)) {
+                       bt_shell_printf("Too much data\n");
+                       ad_clear_manufacturer();
+                       return;
                }
 
-               val = strtol(w.we_wordv[i], &endptr, 0);
+               val = strtol(argv[i], &endptr, 0);
                if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
-                       rl_printf("Invalid value at index %d\n", i);
-                       ad_clear_service();
-                       goto done;
+                       bt_shell_printf("Invalid value at index %d\n", i);
+                       ad_clear_manufacturer();
+                       return;
                }
 
-               ad_manufacturer_data[ad_manufacturer_data_len] = val;
-               ad_manufacturer_data_len++;
+               data->data[data->len] = val;
+               data->len++;
        }
 
-done:
-       wordfree(&w);
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE,
+                                                       "ManufacturerData");
+}
+
+void ad_advertise_tx_power(DBusConnection *conn, bool value)
+{
+       if (ad.tx_power == value)
+               return;
+
+       ad.tx_power = value;
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
 }
 
+void ad_advertise_name(DBusConnection *conn, bool value)
+{
+       if (ad.name == value)
+               return;
+
+       ad.name = value;
+
+       if (!value) {
+               g_free(ad.local_name);
+               ad.local_name = NULL;
+       }
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
+}
 
-void ad_advertise_tx_power(gboolean value)
+void ad_advertise_local_name(DBusConnection *conn, const char *name)
 {
-       ad_tx_power = value;
+       if (ad.local_name && !strcmp(name, ad.local_name))
+               return;
+
+       g_free(ad.local_name);
+       ad.local_name = strdup(name);
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "LocalName");
+}
+
+void ad_advertise_appearance(DBusConnection *conn, bool value)
+{
+       if (ad.appearance == value)
+               return;
+
+       ad.appearance = value;
+
+       if (!value)
+               ad.local_appearance = UINT16_MAX;
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Includes");
+}
+
+void ad_advertise_local_appearance(DBusConnection *conn, uint16_t value)
+{
+       if (ad.local_appearance == value)
+               return;
+
+       ad.local_appearance = value;
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Appearance");
+}
+
+void ad_advertise_duration(DBusConnection *conn, uint16_t value)
+{
+       if (ad.duration == value)
+               return;
+
+       ad.duration = value;
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Duration");
+}
+
+void ad_advertise_timeout(DBusConnection *conn, uint16_t value)
+{
+       if (ad.timeout == value)
+               return;
+
+       ad.timeout = value;
+
+       g_dbus_emit_property_changed(conn, AD_PATH, AD_IFACE, "Timeout");
 }
index 8638465..d7472e1 100755 (executable)
 void ad_register(DBusConnection *conn, GDBusProxy *manager, const char *type);
 void ad_unregister(DBusConnection *conn, GDBusProxy *manager);
 
-void ad_advertise_uuids(const char *arg);
-void ad_advertise_service(const char *arg);
-void ad_advertise_manufacturer(const char *arg);
-void ad_advertise_tx_power(gboolean value);
+void ad_advertise_uuids(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_service(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_manufacturer(DBusConnection *conn, int argc, char *argv[]);
+void ad_advertise_tx_power(DBusConnection *conn, bool value);
+void ad_advertise_name(DBusConnection *conn, bool value);
+void ad_advertise_appearance(DBusConnection *conn, bool value);
+void ad_advertise_local_name(DBusConnection *conn, const char *name);
+void ad_advertise_local_appearance(DBusConnection *conn, uint16_t value);
+void ad_advertise_duration(DBusConnection *conn, uint16_t value);
+void ad_advertise_timeout(DBusConnection *conn, uint16_t value);
index 2cbc292..70c3248 100755 (executable)
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <readline/readline.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
 
+#include <glib.h>
+
+#include "src/shared/shell.h"
 #include "gdbus/gdbus.h"
-#include "display.h"
 #include "agent.h"
 
 #define AGENT_PATH "/org/bluez/agent"
 static gboolean agent_registered = FALSE;
 static const char *agent_capability = NULL;
 static DBusMessage *pending_message = NULL;
-static char *agent_saved_prompt = NULL;
-static int agent_saved_point = 0;
-
-static void agent_prompt(const char *msg)
-{
-       char *prompt;
-
-       /* Normal use should not prompt for user input to the agent a second
-        * time before it releases the prompt, but we take a safe action. */
-       if (agent_saved_prompt)
-               return;
-
-       agent_saved_point = rl_point;
-       agent_saved_prompt = g_strdup(rl_prompt);
-
-       rl_set_prompt("");
-       rl_redisplay();
-
-       prompt = g_strdup_printf(AGENT_PROMPT "%s", msg);
-       rl_set_prompt(prompt);
-       g_free(prompt);
-
-       rl_replace_line("", 0);
-       rl_redisplay();
-}
 
 static void agent_release_prompt(void)
 {
-       if (!agent_saved_prompt)
+       if (!pending_message)
                return;
 
-       /* This will cause rl_expand_prompt to re-run over the last prompt, but
-        * our prompt doesn't expand anyway. */
-       rl_set_prompt(agent_saved_prompt);
-       rl_replace_line("", 0);
-       rl_point = agent_saved_point;
-       rl_redisplay();
-
-       g_free(agent_saved_prompt);
-       agent_saved_prompt = NULL;
+       bt_shell_release_prompt("");
 }
 
 dbus_bool_t agent_completion(void)
@@ -91,15 +62,19 @@ dbus_bool_t agent_completion(void)
        return TRUE;
 }
 
-static void pincode_response(DBusConnection *conn, const char *input)
+static void pincode_response(const char *input, void *user_data)
 {
+       DBusConnection *conn = user_data;
+
        g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input,
                                                        DBUS_TYPE_INVALID);
 }
 
-static void passkey_response(DBusConnection *conn, const char *input)
+static void passkey_response(const char *input, void *user_data)
 {
+       DBusConnection *conn = user_data;
        dbus_uint32_t passkey;
+
        if (sscanf(input, "%u", &passkey) == 1)
                g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32,
                                                &passkey, DBUS_TYPE_INVALID);
@@ -111,8 +86,10 @@ static void passkey_response(DBusConnection *conn, const char *input)
                                        "org.bluez.Error.Canceled", NULL);
 }
 
-static void confirm_response(DBusConnection *conn, const char *input)
+static void confirm_response(const char *input, void *user_data)
 {
+       DBusConnection *conn = user_data;
+
        if (!strcmp(input, "yes"))
                g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID);
        else if (!strcmp(input, "no"))
@@ -123,37 +100,6 @@ static void confirm_response(DBusConnection *conn, const char *input)
                                        "org.bluez.Error.Canceled", NULL);
 }
 
-dbus_bool_t agent_input(DBusConnection *conn, const char *input)
-{
-       const char *member;
-
-       if (!pending_message)
-               return FALSE;
-
-       agent_release_prompt();
-
-       member = dbus_message_get_member(pending_message);
-
-       if (!strcmp(member, "RequestPinCode"))
-               pincode_response(conn, input);
-       else if (!strcmp(member, "RequestPasskey"))
-               passkey_response(conn, input);
-       else if (!strcmp(member, "RequestConfirmation"))
-               confirm_response(conn, input);
-       else if (!strcmp(member, "RequestAuthorization"))
-               confirm_response(conn, input);
-       else if (!strcmp(member, "AuthorizeService"))
-               confirm_response(conn, input);
-       else
-               g_dbus_send_error(conn, pending_message,
-                                       "org.bluez.Error.Canceled", NULL);
-
-       dbus_message_unref(pending_message);
-       pending_message = NULL;
-
-       return TRUE;
-}
-
 static void agent_release(DBusConnection *conn)
 {
        agent_registered = FALSE;
@@ -172,7 +118,7 @@ static void agent_release(DBusConnection *conn)
 static DBusMessage *release_agent(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
-       rl_printf("Agent released\n");
+       bt_shell_printf("Agent released\n");
 
        agent_release(conn);
 
@@ -184,12 +130,13 @@ static DBusMessage *request_pincode(DBusConnection *conn,
 {
        const char *device;
 
-       rl_printf("Request PIN code\n");
+       bt_shell_printf("Request PIN code\n");
 
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                                        DBUS_TYPE_INVALID);
 
-       agent_prompt("Enter PIN code: ");
+       bt_shell_prompt_input("agent", "Enter PIN code:", pincode_response,
+                                                               conn);
 
        pending_message = dbus_message_ref(msg);
 
@@ -205,7 +152,7 @@ static DBusMessage *display_pincode(DBusConnection *conn,
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID);
 
-       rl_printf(AGENT_PROMPT "PIN code: %s\n", pincode);
+       bt_shell_printf(AGENT_PROMPT "PIN code: %s\n", pincode);
 
        return dbus_message_new_method_return(msg);
 }
@@ -215,12 +162,13 @@ static DBusMessage *request_passkey(DBusConnection *conn,
 {
        const char *device;
 
-       rl_printf("Request passkey\n");
+       bt_shell_printf("Request passkey\n");
 
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                                        DBUS_TYPE_INVALID);
 
-       agent_prompt("Enter passkey (number in 0-999999): ");
+       bt_shell_prompt_input("agent", "Enter passkey (number in 0-999999):",
+                                               passkey_response, conn);
 
        pending_message = dbus_message_ref(msg);
 
@@ -245,7 +193,7 @@ static DBusMessage *display_passkey(DBusConnection *conn,
        if (entered > strlen(passkey_full))
                entered = strlen(passkey_full);
 
-       rl_printf(AGENT_PROMPT "Passkey: "
+       bt_shell_printf(AGENT_PROMPT "Passkey: "
                        COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF,
                                entered, passkey_full, passkey_full + entered);
 
@@ -259,13 +207,13 @@ static DBusMessage *request_confirmation(DBusConnection *conn,
        dbus_uint32_t passkey;
        char *str;
 
-       rl_printf("Request confirmation\n");
+       bt_shell_printf("Request confirmation\n");
 
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID);
 
-       str = g_strdup_printf("Confirm passkey %06u (yes/no): ", passkey);
-       agent_prompt(str);
+       str = g_strdup_printf("Confirm passkey %06u (yes/no):", passkey);
+       bt_shell_prompt_input("agent", str, confirm_response, conn);
        g_free(str);
 
        pending_message = dbus_message_ref(msg);
@@ -278,12 +226,13 @@ static DBusMessage *request_authorization(DBusConnection *conn,
 {
        const char *device;
 
-       rl_printf("Request authorization\n");
+       bt_shell_printf("Request authorization\n");
 
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                                        DBUS_TYPE_INVALID);
 
-       agent_prompt("Accept pairing (yes/no): ");
+       bt_shell_prompt_input("agent", "Accept pairing (yes/no):",
+                                       confirm_response, conn);
 
        pending_message = dbus_message_ref(msg);
 
@@ -296,13 +245,13 @@ static DBusMessage *authorize_service(DBusConnection *conn,
        const char *device, *uuid;
        char *str;
 
-       rl_printf("Authorize service\n");
+       bt_shell_printf("Authorize service\n");
 
        dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
                                DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
 
-       str = g_strdup_printf("Authorize service %s (yes/no): ", uuid);
-       agent_prompt(str);
+       str = g_strdup_printf("Authorize service %s (yes/no):", uuid);
+       bt_shell_prompt_input("agent", str, confirm_response, conn);
        g_free(str);
 
        pending_message = dbus_message_ref(msg);
@@ -313,7 +262,7 @@ static DBusMessage *authorize_service(DBusConnection *conn,
 static DBusMessage *cancel_request(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
-       rl_printf("Request canceled\n");
+       bt_shell_printf("Request canceled\n");
 
        agent_release_prompt();
        dbus_message_unref(pending_message);
@@ -368,14 +317,14 @@ static void register_agent_reply(DBusMessage *message, void *user_data)
 
        if (dbus_set_error_from_message(&error, message) == FALSE) {
                agent_registered = TRUE;
-               rl_printf("Agent registered\n");
+               bt_shell_printf("Agent registered\n");
        } else {
-               rl_printf("Failed to register agent: %s\n", error.name);
+               bt_shell_printf("Failed to register agent: %s\n", error.name);
                dbus_error_free(&error);
 
                if (g_dbus_unregister_interface(conn, AGENT_PATH,
                                                AGENT_INTERFACE) == FALSE)
-                       rl_printf("Failed to unregister agent object\n");
+                       bt_shell_printf("Failed to unregister agent object\n");
        }
 }
 
@@ -384,7 +333,7 @@ void agent_register(DBusConnection *conn, GDBusProxy *manager,
 
 {
        if (agent_registered == TRUE) {
-               rl_printf("Agent is already registered\n");
+               bt_shell_printf("Agent is already registered\n");
                return;
        }
 
@@ -393,7 +342,7 @@ void agent_register(DBusConnection *conn, GDBusProxy *manager,
        if (g_dbus_register_interface(conn, AGENT_PATH,
                                        AGENT_INTERFACE, methods,
                                        NULL, NULL, NULL, NULL) == FALSE) {
-               rl_printf("Failed to register agent object\n");
+               bt_shell_printf("Failed to register agent object\n");
                return;
        }
 
@@ -401,7 +350,7 @@ void agent_register(DBusConnection *conn, GDBusProxy *manager,
                                                register_agent_setup,
                                                register_agent_reply,
                                                conn, NULL) == FALSE) {
-               rl_printf("Failed to call register agent method\n");
+               bt_shell_printf("Failed to call register agent method\n");
                return;
        }
 
@@ -423,10 +372,10 @@ static void unregister_agent_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == FALSE) {
-               rl_printf("Agent unregistered\n");
+               bt_shell_printf("Agent unregistered\n");
                agent_release(conn);
        } else {
-               rl_printf("Failed to unregister agent: %s\n", error.name);
+               bt_shell_printf("Failed to unregister agent: %s\n", error.name);
                dbus_error_free(&error);
        }
 }
@@ -434,12 +383,12 @@ static void unregister_agent_reply(DBusMessage *message, void *user_data)
 void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
 {
        if (agent_registered == FALSE) {
-               rl_printf("No agent is registered\n");
+               bt_shell_printf("No agent is registered\n");
                return;
        }
 
        if (!manager) {
-               rl_printf("Agent unregistered\n");
+               bt_shell_printf("Agent unregistered\n");
                agent_release(conn);
                return;
        }
@@ -448,7 +397,7 @@ void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
                                                unregister_agent_setup,
                                                unregister_agent_reply,
                                                conn, NULL) == FALSE) {
-               rl_printf("Failed to call unregister agent method\n");
+               bt_shell_printf("Failed to call unregister agent method\n");
                return;
        }
 }
@@ -467,18 +416,19 @@ static void request_default_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to request default agent: %s\n", error.name);
+               bt_shell_printf("Failed to request default agent: %s\n",
+                                                       error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Default agent request successful\n");
+       bt_shell_printf("Default agent request successful\n");
 }
 
 void agent_default(DBusConnection *conn, GDBusProxy *manager)
 {
        if (agent_registered == FALSE) {
-               rl_printf("No agent is registered\n");
+               bt_shell_printf("No agent is registered\n");
                return;
        }
 
@@ -486,7 +436,7 @@ void agent_default(DBusConnection *conn, GDBusProxy *manager)
                                                request_default_setup,
                                                request_default_reply,
                                                NULL, NULL) == FALSE) {
-               rl_printf("Failed to call request default agent method\n");
+               bt_shell_printf("Failed to call RequestDefaultAgent method\n");
                return;
        }
 }
index 0fbe8e5..30f302c 100755 (executable)
@@ -27,4 +27,3 @@ void agent_unregister(DBusConnection *conn, GDBusProxy *manager);
 void agent_default(DBusConnection *conn, GDBusProxy *manager);
 
 dbus_bool_t agent_completion(void);
-dbus_bool_t agent_input(DBusConnection *conn, const char *input);
index d85b9d0..bd23e8c 100755 (executable)
 
 #include "display.h"
 
+static char *saved_prompt = NULL;
+static int saved_point = 0;
+static rl_prompt_input_func saved_func = NULL;
+static void *saved_user_data = NULL;
+
 void rl_printf(const char *fmt, ...)
 {
        va_list args;
@@ -104,3 +109,59 @@ void rl_hexdump(const unsigned char *buf, size_t len)
                rl_printf("%s\n", str);
        }
 }
+
+void rl_prompt_input(const char *label, const char *msg,
+                               rl_prompt_input_func func, void *user_data)
+{
+       char prompt[256];
+
+       /* Normal use should not prompt for user input to the value a second
+        * time before it releases the prompt, but we take a safe action. */
+       if (saved_prompt)
+               return;
+
+       saved_point = rl_point;
+       saved_prompt = strdup(rl_prompt);
+       saved_func = func;
+       saved_user_data = user_data;
+
+       rl_set_prompt("");
+       rl_redisplay();
+
+       memset(prompt, 0, sizeof(prompt));
+       snprintf(prompt, sizeof(prompt), COLOR_RED "[%s]" COLOR_OFF " %s ",
+                                                               label, msg);
+       rl_set_prompt(prompt);
+
+       rl_replace_line("", 0);
+       rl_redisplay();
+}
+
+int rl_release_prompt(const char *input)
+{
+       rl_prompt_input_func func;
+       void *user_data;
+
+       if (!saved_prompt)
+               return -1;
+
+       /* This will cause rl_expand_prompt to re-run over the last prompt, but
+        * our prompt doesn't expand anyway. */
+       rl_set_prompt(saved_prompt);
+       rl_replace_line("", 0);
+       rl_point = saved_point;
+       rl_redisplay();
+
+       free(saved_prompt);
+       saved_prompt = NULL;
+
+       func = saved_func;
+       user_data = saved_user_data;
+
+       saved_func = NULL;
+       saved_user_data = NULL;
+
+       func(input, user_data);
+
+       return 0;
+}
index 88dbbd0..e991d19 100755 (executable)
@@ -31,3 +31,8 @@
 
 void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
 void rl_hexdump(const unsigned char *buf, size_t len);
+
+typedef void (*rl_prompt_input_func) (const char *input, void *user_data);
+void rl_prompt_input(const char *label, const char *msg,
+                               rl_prompt_input_func func, void *user_data);
+int rl_release_prompt(const char *input);
index 37f222d..224a78a 100755 (executable)
 #include <stdlib.h>
 #include <stdbool.h>
 #include <sys/uio.h>
-#include <wordexp.h>
+#include <fcntl.h>
+#include <string.h>
 
-#include <readline/readline.h>
-#include <readline/history.h>
 #include <glib.h>
 
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/io.h"
+#include "src/shared/shell.h"
 #include "gdbus/gdbus.h"
-#include "monitor/uuid.h"
-#include "display.h"
 #include "gatt.h"
 
-#define PROFILE_PATH "/org/bluez/profile"
+#define APP_PATH "/org/bluez/app"
 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
+#define SERVICE_INTERFACE "org.bluez.GattService1"
+#define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
+#define DESC_INTERFACE "org.bluez.GattDescriptor1"
 
 /* String display constants */
 #define COLORED_NEW    COLOR_GREEN "NEW" COLOR_OFF
 #define COLORED_CHG    COLOR_YELLOW "CHG" COLOR_OFF
 #define COLORED_DEL    COLOR_RED "DEL" COLOR_OFF
 
+struct desc {
+       struct chrc *chrc;
+       char *path;
+       char *uuid;
+       char **flags;
+       int value_len;
+       uint8_t *value;
+};
+
+struct chrc {
+       struct service *service;
+       char *path;
+       char *uuid;
+       char **flags;
+       bool notifying;
+       GList *descs;
+       int value_len;
+       uint8_t *value;
+       uint16_t mtu;
+       struct io *write_io;
+       struct io *notify_io;
+};
+
+struct service {
+       DBusConnection *conn;
+       char *path;
+       char *uuid;
+       bool primary;
+       GList *chrcs;
+};
+
+static GList *local_services;
 static GList *services;
 static GList *characteristics;
 static GList *descriptors;
 static GList *managers;
+static GList *uuids;
+
+struct pipe_io {
+       GDBusProxy *proxy;
+       struct io *io;
+       uint16_t mtu;
+};
+
+static struct pipe_io write_io;
+static struct pipe_io notify_io;
+
+static void print_service(struct service *service, const char *description)
+{
+       const char *text;
+
+       text = bt_uuidstr_to_str(service->uuid);
+       if (!text)
+               bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
+                                       description ? "[" : "",
+                                       description ? : "",
+                                       description ? "] " : "",
+                                       service->primary ? "Primary" :
+                                       "Secondary",
+                                       service->path, service->uuid);
+       else
+               bt_shell_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
+                                       description ? "[" : "",
+                                       description ? : "",
+                                       description ? "] " : "",
+                                       service->primary ? "Primary" :
+                                       "Secondary",
+                                       service->path, service->uuid, text);
+}
 
-static void print_service(GDBusProxy *proxy, const char *description)
+static void print_service_proxy(GDBusProxy *proxy, const char *description)
 {
+       struct service service;
        DBusMessageIter iter;
-       const char *uuid, *text;
+       const char *uuid;
        dbus_bool_t primary;
 
        if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
@@ -71,30 +141,18 @@ static void print_service(GDBusProxy *proxy, const char *description)
 
        dbus_message_iter_get_basic(&iter, &primary);
 
-       text = uuidstr_to_str(uuid);
-       if (!text)
-               rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n",
-                                       description ? "[" : "",
-                                       description ? : "",
-                                       description ? "] " : "",
-                                       primary ? "Primary" : "Secondary",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid);
-       else
-               rl_printf("%s%s%s%s Service\n\t%s\n\t%s\n\t%s\n",
-                                       description ? "[" : "",
-                                       description ? : "",
-                                       description ? "] " : "",
-                                       primary ? "Primary" : "Secondary",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid, text);
+       service.path = (char *) g_dbus_proxy_get_path(proxy);
+       service.uuid = (char *) uuid;
+       service.primary = primary;
+
+       print_service(&service, description);
 }
 
 void gatt_add_service(GDBusProxy *proxy)
 {
        services = g_list_append(services, proxy);
 
-       print_service(proxy, COLORED_NEW);
+       print_service_proxy(proxy, COLORED_NEW);
 }
 
 void gatt_remove_service(GDBusProxy *proxy)
@@ -107,37 +165,46 @@ void gatt_remove_service(GDBusProxy *proxy)
 
        services = g_list_delete_link(services, l);
 
-       print_service(proxy, COLORED_DEL);
+       print_service_proxy(proxy, COLORED_DEL);
 }
 
-static void print_characteristic(GDBusProxy *proxy, const char *description)
+static void print_chrc(struct chrc *chrc, const char *description)
 {
-       DBusMessageIter iter;
-       const char *uuid, *text;
-
-       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
-               return;
-
-       dbus_message_iter_get_basic(&iter, &uuid);
+       const char *text;
 
-       text = uuidstr_to_str(uuid);
+       text = bt_uuidstr_to_str(chrc->uuid);
        if (!text)
-               rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
+               bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n",
                                        description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid);
+                                       chrc->path, chrc->uuid);
        else
-               rl_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
+               bt_shell_printf("%s%s%sCharacteristic\n\t%s\n\t%s\n\t%s\n",
                                        description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid, text);
+                                       chrc->path, chrc->uuid, text);
+}
+
+static void print_characteristic(GDBusProxy *proxy, const char *description)
+{
+       struct chrc chrc;
+       DBusMessageIter iter;
+       const char *uuid;
+
+       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       chrc.path = (char *) g_dbus_proxy_get_path(proxy);
+       chrc.uuid = (char *) uuid;
+
+       print_chrc(&chrc, description);
 }
 
-static gboolean characteristic_is_child(GDBusProxy *characteristic)
+static gboolean chrc_is_child(GDBusProxy *characteristic)
 {
        GList *l;
        DBusMessageIter iter;
@@ -162,7 +229,7 @@ static gboolean characteristic_is_child(GDBusProxy *characteristic)
 
 void gatt_add_characteristic(GDBusProxy *proxy)
 {
-       if (!characteristic_is_child(proxy))
+       if (!chrc_is_child(proxy))
                return;
 
        characteristics = g_list_append(characteristics, proxy);
@@ -170,6 +237,18 @@ void gatt_add_characteristic(GDBusProxy *proxy)
        print_characteristic(proxy, COLORED_NEW);
 }
 
+static void notify_io_destroy(void)
+{
+       io_destroy(notify_io.io);
+       memset(&notify_io, 0, sizeof(notify_io));
+}
+
+static void write_io_destroy(void)
+{
+       io_destroy(write_io.io);
+       memset(&write_io, 0, sizeof(write_io));
+}
+
 void gatt_remove_characteristic(GDBusProxy *proxy)
 {
        GList *l;
@@ -181,33 +260,47 @@ void gatt_remove_characteristic(GDBusProxy *proxy)
        characteristics = g_list_delete_link(characteristics, l);
 
        print_characteristic(proxy, COLORED_DEL);
+
+       if (write_io.proxy == proxy)
+               write_io_destroy();
+       else if (notify_io.proxy == proxy)
+               notify_io_destroy();
 }
 
-static void print_descriptor(GDBusProxy *proxy, const char *description)
+static void print_desc(struct desc *desc, const char *description)
 {
-       DBusMessageIter iter;
-       const char *uuid, *text;
-
-       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
-               return;
+       const char *text;
 
-       dbus_message_iter_get_basic(&iter, &uuid);
-
-       text = uuidstr_to_str(uuid);
+       text = bt_uuidstr_to_str(desc->uuid);
        if (!text)
-               rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
+               bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n",
                                        description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid);
+                                       desc->path, desc->uuid);
        else
-               rl_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
+               bt_shell_printf("%s%s%sDescriptor\n\t%s\n\t%s\n\t%s\n",
                                        description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
-                                       g_dbus_proxy_get_path(proxy),
-                                       uuid, text);
+                                       desc->path, desc->uuid, text);
+}
+
+static void print_descriptor(GDBusProxy *proxy, const char *description)
+{
+       struct desc desc;
+       DBusMessageIter iter;
+       const char *uuid;
+
+       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       desc.path = (char *) g_dbus_proxy_get_path(proxy);
+       desc.uuid = (char *) uuid;
+
+       print_desc(&desc, description);
 }
 
 static gboolean descriptor_is_child(GDBusProxy *characteristic)
@@ -270,7 +363,7 @@ static void list_attributes(const char *path, GList *source)
                        continue;
 
                if (source == services) {
-                       print_service(proxy, NULL);
+                       print_service_proxy(proxy, NULL);
                        list_attributes(proxy_path, characteristics);
                } else if (source == characteristics) {
                        print_characteristic(proxy, NULL);
@@ -299,7 +392,7 @@ static GDBusProxy *select_proxy(const char *path, GList *source)
        return NULL;
 }
 
-GDBusProxy *gatt_select_attribute(const char *path)
+static GDBusProxy *select_attribute(const char *path)
 {
        GDBusProxy *proxy;
 
@@ -314,6 +407,62 @@ GDBusProxy *gatt_select_attribute(const char *path)
        return select_proxy(path, descriptors);
 }
 
+static GDBusProxy *select_proxy_by_uuid(GDBusProxy *parent, const char *uuid,
+                                       GList *source)
+{
+       GList *l;
+       const char *value;
+       DBusMessageIter iter;
+
+       for (l = source; l; l = g_list_next(l)) {
+               GDBusProxy *proxy = l->data;
+
+               if (parent && !g_str_has_prefix(g_dbus_proxy_get_path(proxy),
+                                               g_dbus_proxy_get_path(parent)))
+                       continue;
+
+               if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+                       continue;
+
+               dbus_message_iter_get_basic(&iter, &value);
+
+               if (strcasecmp(uuid, value) == 0)
+                       return proxy;
+       }
+
+       return NULL;
+}
+
+static GDBusProxy *select_attribute_by_uuid(GDBusProxy *parent,
+                                                       const char *uuid)
+{
+       GDBusProxy *proxy;
+
+       proxy = select_proxy_by_uuid(parent, uuid, services);
+       if (proxy)
+               return proxy;
+
+       proxy = select_proxy_by_uuid(parent, uuid, characteristics);
+       if (proxy)
+               return proxy;
+
+       return select_proxy_by_uuid(parent, uuid, descriptors);
+}
+
+GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *arg)
+{
+       if (arg[0] == '/')
+               return select_attribute(arg);
+
+       if (parent) {
+               GDBusProxy *proxy = select_attribute_by_uuid(parent, arg);
+               if (proxy)
+                       return proxy;
+       }
+
+       return select_attribute_by_uuid(parent, arg);
+}
+
 static char *attribute_generator(const char *text, int state, GList *source)
 {
        static int index, len;
@@ -372,7 +521,7 @@ static void read_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to read: %s\n", error.name);
+               bt_shell_printf("Failed to read: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -380,7 +529,7 @@ static void read_reply(DBusMessage *message, void *user_data)
        dbus_message_iter_init(message, &iter);
 
        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
-               rl_printf("Invalid response to read\n");
+               bt_shell_printf("Invalid response to read\n");
                return;
        }
 
@@ -388,11 +537,11 @@ static void read_reply(DBusMessage *message, void *user_data)
        dbus_message_iter_get_fixed_array(&array, &value, &len);
 
        if (len < 0) {
-               rl_printf("Unable to parse value\n");
+               bt_shell_printf("Unable to parse value\n");
                return;
        }
 
-       rl_hexdump(value, len);
+       bt_shell_hexdump(value, len);
 }
 
 static void read_setup(DBusMessageIter *iter, void *user_data)
@@ -413,11 +562,11 @@ static void read_attribute(GDBusProxy *proxy)
 {
        if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to read\n");
+               bt_shell_printf("Failed to read\n");
                return;
        }
 
-       rl_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
 }
 
 void gatt_read_attribute(GDBusProxy *proxy)
@@ -431,7 +580,7 @@ void gatt_read_attribute(GDBusProxy *proxy)
                return;
        }
 
-       rl_printf("Unable to read attribute %s\n",
+       bt_shell_printf("Unable to read attribute %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -442,7 +591,7 @@ static void write_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to write: %s\n", error.name);
+               bt_shell_printf("Failed to write: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -483,13 +632,13 @@ static void write_attribute(GDBusProxy *proxy, char *arg)
                        continue;
 
                if (i >= G_N_ELEMENTS(value)) {
-                       rl_printf("Too much data\n");
+                       bt_shell_printf("Too much data\n");
                        return;
                }
 
                val = strtol(entry, &endptr, 0);
                if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
-                       rl_printf("Invalid value at index %d\n", i);
+                       bt_shell_printf("Invalid value at index %d\n", i);
                        return;
                }
 
@@ -499,13 +648,24 @@ static void write_attribute(GDBusProxy *proxy, char *arg)
        iov.iov_base = value;
        iov.iov_len = i;
 
+       /* Write using the fd if it has been acquired and fit the MTU */
+       if (proxy == write_io.proxy && (write_io.io && write_io.mtu >= i)) {
+               bt_shell_printf("Attempting to write fd %d\n",
+                                               io_get_fd(write_io.io));
+               if (io_send(write_io.io, &iov, 1) < 0) {
+                       bt_shell_printf("Failed to write: %s", strerror(errno));
+                       return;
+               }
+               return;
+       }
+
        if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
                                        write_reply, &iov, NULL) == FALSE) {
-               rl_printf("Failed to write\n");
+               bt_shell_printf("Failed to write\n");
                return;
        }
 
-       rl_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
 }
 
 void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
@@ -519,8 +679,208 @@ void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
                return;
        }
 
-       rl_printf("Unable to write attribute %s\n",
+       bt_shell_printf("Unable to write attribute %s\n",
+                                               g_dbus_proxy_get_path(proxy));
+}
+
+static bool pipe_read(struct io *io, void *user_data)
+{
+       struct chrc *chrc = user_data;
+       uint8_t buf[512];
+       int fd = io_get_fd(io);
+       ssize_t bytes_read;
+
+       if (io != notify_io.io && !chrc)
+               return true;
+
+       bytes_read = read(fd, buf, sizeof(buf));
+       if (bytes_read < 0)
+               return false;
+
+       if (chrc)
+               bt_shell_printf("[" COLORED_CHG "] Attribute %s written:\n",
+                                                       chrc->path);
+       else
+               bt_shell_printf("[" COLORED_CHG "] %s Notification:\n",
+                               g_dbus_proxy_get_path(notify_io.proxy));
+
+       bt_shell_hexdump(buf, bytes_read);
+
+       return true;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       if (chrc) {
+               bt_shell_printf("Attribute %s Write pipe closed\n", chrc->path);
+               if (chrc->write_io) {
+                       io_destroy(chrc->write_io);
+                       chrc->write_io = NULL;
+               }
+               return false;
+       }
+
+       bt_shell_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
+
+       if (io == notify_io.io)
+               notify_io_destroy();
+       else
+               write_io_destroy();
+
+       return false;
+}
+
+static struct io *pipe_io_new(int fd, void *user_data)
+{
+       struct io *io;
+
+       io = io_new(fd);
+
+       io_set_close_on_destroy(io, true);
+
+       io_set_read_handler(io, pipe_read, user_data, NULL);
+
+       io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
+
+       return io;
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+       int fd;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to acquire write: %s\n", error.name);
+               dbus_error_free(&error);
+               write_io.proxy = NULL;
+               return;
+       }
+
+       if (write_io.io)
+               write_io_destroy();
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &write_io.mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               bt_shell_printf("Invalid AcquireWrite response\n");
+               return;
+       }
+
+       bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_io.mtu);
+
+       write_io.io = pipe_io_new(fd, NULL);
+}
+
+static void acquire_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);
+}
+
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
+{
+       const char *iface;
+
+       iface = g_dbus_proxy_get_interface(proxy);
+       if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+               bt_shell_printf("Unable to acquire write: %s not a characteristic\n",
+                                               g_dbus_proxy_get_path(proxy));
+               return;
+       }
+
+       if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup,
+                               acquire_write_reply, NULL, NULL) == FALSE) {
+               bt_shell_printf("Failed to AcquireWrite\n");
+               return;
+       }
+
+       write_io.proxy = proxy;
+}
+
+void gatt_release_write(GDBusProxy *proxy, const char *arg)
+{
+       if (proxy != write_io.proxy || !write_io.io) {
+               bt_shell_printf("Write not acquired\n");
+               return;
+       }
+
+       write_io_destroy();
+}
+
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+       int fd;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to acquire notify: %s\n", error.name);
+               dbus_error_free(&error);
+               write_io.proxy = NULL;
+               return;
+       }
+
+       if (notify_io.io) {
+               io_destroy(notify_io.io);
+               notify_io.io = NULL;
+       }
+
+       notify_io.mtu = 0;
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &notify_io.mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               bt_shell_printf("Invalid AcquireNotify response\n");
+               return;
+       }
+
+       bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_io.mtu);
+
+       notify_io.io = pipe_io_new(fd, NULL);
+}
+
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
+{
+       const char *iface;
+
+       iface = g_dbus_proxy_get_interface(proxy);
+       if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
+               bt_shell_printf("Unable to acquire notify: %s not a characteristic\n",
                                                g_dbus_proxy_get_path(proxy));
+               return;
+       }
+
+       if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup,
+                               acquire_notify_reply, NULL, NULL) == FALSE) {
+               bt_shell_printf("Failed to AcquireNotify\n");
+               return;
+       }
+
+       notify_io.proxy = proxy;
+}
+
+void gatt_release_notify(GDBusProxy *proxy, const char *arg)
+{
+       if (proxy != notify_io.proxy || !notify_io.io) {
+               bt_shell_printf("Notify not acquired\n");
+               return;
+       }
+
+       notify_io_destroy();
 }
 
 static void notify_reply(DBusMessage *message, void *user_data)
@@ -531,13 +891,13 @@ static void notify_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to %s notify: %s\n",
+               bt_shell_printf("Failed to %s notify: %s\n",
                                enable ? "start" : "stop", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
+       bt_shell_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
 }
 
 static void notify_attribute(GDBusProxy *proxy, bool enable)
@@ -551,7 +911,7 @@ static void notify_attribute(GDBusProxy *proxy, bool enable)
 
        if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
                                GUINT_TO_POINTER(enable), NULL) == FALSE) {
-               rl_printf("Failed to %s notify\n", enable ? "start" : "stop");
+               bt_shell_printf("Failed to %s notify\n", enable ? "start" : "stop");
                return;
        }
 }
@@ -566,25 +926,17 @@ void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
                return;
        }
 
-       rl_printf("Unable to notify attribute %s\n",
+       bt_shell_printf("Unable to notify attribute %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
-static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+static void register_app_setup(DBusMessageIter *iter, void *user_data)
 {
-       wordexp_t *w = user_data;
-       DBusMessageIter uuids, opt;
-       const char *path = PROFILE_PATH;
-       size_t i;
+       DBusMessageIter opt;
+       const char *path = "/";
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 
-       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &uuids);
-       for (i = 0; i < w->we_wordc; i++)
-               dbus_message_iter_append_basic(&uuids, DBUS_TYPE_STRING,
-                                                       &w->we_wordv[i]);
-       dbus_message_iter_close_container(iter, &uuids);
-
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
                                        DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
                                        DBUS_TYPE_STRING_AS_STRING
@@ -595,19 +947,19 @@ static void register_profile_setup(DBusMessageIter *iter, void *user_data)
 
 }
 
-static void register_profile_reply(DBusMessage *message, void *user_data)
+static void register_app_reply(DBusMessage *message, void *user_data)
 {
        DBusError error;
 
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to register profile: %s\n", error.name);
+               bt_shell_printf("Failed to register application: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Profile registered\n");
+       bt_shell_printf("Application registered\n");
 }
 
 void gatt_add_manager(GDBusProxy *proxy)
@@ -632,7 +984,7 @@ static int match_proxy(const void *a, const void *b)
 static DBusMessage *release_profile(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
-       g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+       g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
 
        return dbus_message_new_method_return(msg);
 }
@@ -642,34 +994,65 @@ static const GDBusMethodTable methods[] = {
        { }
 };
 
-void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
-                                                               wordexp_t *w)
+static gboolean get_uuids(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       DBusMessageIter entry;
+       GList *uuid;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_STRING_AS_STRING, &entry);
+
+       for (uuid = uuids; uuid; uuid = g_list_next(uuid->next))
+               dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+                                                       &uuid->data);
+
+       dbus_message_iter_close_container(iter, &entry);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable properties[] = {
+       { "UUIDs", "as", get_uuids },
+       { }
+};
+
+void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
+                                       int argc, char *argv[])
 {
        GList *l;
+       int i;
 
        l = g_list_find_custom(managers, proxy, match_proxy);
        if (!l) {
-               rl_printf("Unable to find GattManager proxy\n");
+               bt_shell_printf("Unable to find GattManager proxy\n");
                return;
        }
 
-       if (g_dbus_register_interface(conn, PROFILE_PATH,
-                                       PROFILE_INTERFACE, methods,
-                                       NULL, NULL, NULL, NULL) == FALSE) {
-               rl_printf("Failed to register profile object\n");
-               return;
+       for (i = 0; i < argc; i++)
+               uuids = g_list_append(uuids, g_strdup(argv[i]));
+
+       if (uuids) {
+               if (g_dbus_register_interface(conn, APP_PATH,
+                                               PROFILE_INTERFACE, methods,
+                                               NULL, properties, NULL,
+                                               NULL) == FALSE) {
+                       bt_shell_printf("Failed to register application object\n");
+                       return;
+               }
        }
 
-       if (g_dbus_proxy_method_call(l->data, "RegisterProfile",
-                                               register_profile_setup,
-                                               register_profile_reply, w,
+       if (g_dbus_proxy_method_call(l->data, "RegisterApplication",
+                                               register_app_setup,
+                                               register_app_reply, NULL,
                                                NULL) == FALSE) {
-               rl_printf("Failed register profile\n");
+               bt_shell_printf("Failed register application\n");
+               g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
                return;
        }
 }
 
-static void unregister_profile_reply(DBusMessage *message, void *user_data)
+static void unregister_app_reply(DBusMessage *message, void *user_data)
 {
        DBusConnection *conn = user_data;
        DBusError error;
@@ -677,38 +1060,907 @@ static void unregister_profile_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to unregister profile: %s\n", error.name);
+               bt_shell_printf("Failed to unregister application: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Profile unregistered\n");
+       bt_shell_printf("Application unregistered\n");
+
+       if (!uuids)
+               return;
+
+       g_list_free_full(uuids, g_free);
+       uuids = NULL;
 
-       g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+       g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
 }
 
-static void unregister_profile_setup(DBusMessageIter *iter, void *user_data)
+static void unregister_app_setup(DBusMessageIter *iter, void *user_data)
 {
-       const char *path = PROFILE_PATH;
+       const char *path = "/";
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
 }
 
-void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy)
+void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
 {
        GList *l;
 
        l = g_list_find_custom(managers, proxy, match_proxy);
        if (!l) {
-               rl_printf("Unable to find GattManager proxy\n");
+               bt_shell_printf("Unable to find GattManager proxy\n");
                return;
        }
 
-       if (g_dbus_proxy_method_call(l->data, "UnregisterProfile",
-                                               unregister_profile_setup,
-                                               unregister_profile_reply, conn,
+       if (g_dbus_proxy_method_call(l->data, "UnregisterApplication",
+                                               unregister_app_setup,
+                                               unregister_app_reply, conn,
                                                NULL) == FALSE) {
-               rl_printf("Failed unregister profile\n");
+               bt_shell_printf("Failed unregister profile\n");
                return;
        }
 }
+
+static void desc_free(void *data)
+{
+       struct desc *desc = data;
+
+       g_free(desc->path);
+       g_free(desc->uuid);
+       g_strfreev(desc->flags);
+       g_free(desc->value);
+       g_free(desc);
+}
+
+static void desc_unregister(void *data)
+{
+       struct desc *desc = data;
+
+       print_desc(desc, COLORED_DEL);
+
+       g_dbus_unregister_interface(desc->chrc->service->conn, desc->path,
+                                               DESC_INTERFACE);
+}
+
+static void chrc_free(void *data)
+{
+       struct chrc *chrc = data;
+
+       g_list_free_full(chrc->descs, desc_unregister);
+       g_free(chrc->path);
+       g_free(chrc->uuid);
+       g_strfreev(chrc->flags);
+       g_free(chrc->value);
+       g_free(chrc);
+}
+
+static void chrc_unregister(void *data)
+{
+       struct chrc *chrc = data;
+
+       print_chrc(chrc, COLORED_DEL);
+
+       g_dbus_unregister_interface(chrc->service->conn, chrc->path,
+                                               CHRC_INTERFACE);
+}
+
+static void service_free(void *data)
+{
+       struct service *service = data;
+
+       g_list_free_full(service->chrcs, chrc_unregister);
+       g_free(service->path);
+       g_free(service->uuid);
+       g_free(service);
+}
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct service *service = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid);
+
+       return TRUE;
+}
+
+static gboolean service_get_primary(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct service *service = data;
+       dbus_bool_t primary;
+
+       primary = service->primary ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+       { "UUID", "s", service_get_uuid },
+       { "Primary", "b", service_get_primary },
+       { }
+};
+
+static void service_set_primary(const char *input, void *user_data)
+{
+       struct service *service = user_data;
+
+       if (!strcmp(input, "yes"))
+               service->primary = true;
+       else if (!strcmp(input, "no")) {
+               service->primary = false;
+       } else {
+               bt_shell_printf("Invalid option: %s\n", input);
+               local_services = g_list_remove(local_services, service);
+               print_service(service, COLORED_DEL);
+               g_dbus_unregister_interface(service->conn, service->path,
+                                               SERVICE_INTERFACE);
+       }
+}
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[])
+{
+       struct service *service;
+       bool primary = true;
+
+       service = g_new0(struct service, 1);
+       service->conn = conn;
+       service->uuid = g_strdup(argv[1]);
+       service->path = g_strdup_printf("%s/service%p", APP_PATH, service);
+       service->primary = primary;
+
+       if (g_dbus_register_interface(conn, service->path,
+                                       SERVICE_INTERFACE, NULL, NULL,
+                                       service_properties, service,
+                                       service_free) == FALSE) {
+               bt_shell_printf("Failed to register service object\n");
+               service_free(service);
+               return;
+       }
+
+       print_service(service, COLORED_NEW);
+
+       local_services = g_list_append(local_services, service);
+
+       bt_shell_prompt_input(service->path, "Primary (yes/no):", service_set_primary,
+                       service);
+}
+
+static struct service *service_find(const char *pattern)
+{
+       GList *l;
+
+       for (l = local_services; l; l = g_list_next(l)) {
+               struct service *service = l->data;
+
+               /* match object path */
+               if (!strcmp(service->path, pattern))
+                       return service;
+
+               /* match UUID */
+               if (!strcmp(service->uuid, pattern))
+                       return service;
+       }
+
+       return NULL;
+}
+
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[])
+{
+       struct service *service;
+
+       service = service_find(argv[1]);
+       if (!service) {
+               bt_shell_printf("Failed to unregister service object\n");
+               return;
+       }
+
+       local_services = g_list_remove(local_services, service);
+
+       print_service(service, COLORED_DEL);
+
+       g_dbus_unregister_interface(service->conn, service->path,
+                                               SERVICE_INTERFACE);
+}
+
+static gboolean chrc_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid);
+
+       return TRUE;
+}
+
+static gboolean chrc_get_service(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                               &chrc->service->path);
+
+       return TRUE;
+}
+
+static gboolean chrc_get_value(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &chrc->value, chrc->value_len);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static gboolean chrc_get_notifying(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+       dbus_bool_t value;
+
+       value = chrc->notifying ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+       return TRUE;
+}
+
+static gboolean chrc_get_flags(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+       int i;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+       for (i = 0; chrc->flags[i]; i++)
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+                                                       &chrc->flags[i]);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+       dbus_bool_t value;
+
+       value = chrc->write_io ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+       return TRUE;
+}
+
+static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct chrc *chrc = data;
+       int i;
+
+       for (i = 0; chrc->flags[i]; i++) {
+               if (!strcmp("write-without-response", chrc->flags[i]))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct chrc *chrc = data;
+       dbus_bool_t value;
+
+       value = chrc->notify_io ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+       return TRUE;
+}
+
+static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct chrc *chrc = data;
+       int i;
+
+       for (i = 0; chrc->flags[i]; i++) {
+               if (!strcmp("notify", chrc->flags[i]))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static const GDBusPropertyTable chrc_properties[] = {
+       { "UUID", "s", chrc_get_uuid, NULL, NULL },
+       { "Service", "o", chrc_get_service, NULL, NULL },
+       { "Value", "ay", chrc_get_value, NULL, NULL },
+       { "Notifying", "b", chrc_get_notifying, NULL, NULL },
+       { "Flags", "as", chrc_get_flags, NULL, NULL },
+       { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
+                                       chrc_write_acquired_exists },
+       { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL,
+                                       chrc_notify_acquired_exists },
+       { }
+};
+
+static DBusMessage *read_value(DBusMessage *msg, uint8_t *value,
+                                               uint16_t value_len)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+
+       reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &value, value_len);
+       dbus_message_iter_close_container(&iter, &array);
+
+       return reply;
+}
+
+static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       return read_value(msg, chrc->value, chrc->value_len);
+}
+
+static int parse_value_arg(DBusMessageIter *iter, 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 DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+       DBusMessageIter iter;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (parse_value_arg(&iter, &chrc->value, &chrc->value_len))
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.InvalidArguments",
+                                       NULL);
+
+       bt_shell_printf("[" COLORED_CHG "] Attribute %s written" , chrc->path);
+
+       g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static int parse_options(DBusMessageIter *iter, struct chrc *chrc)
+{
+       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;
+               } else if (strcasecmp(key, "MTU") == 0) {
+                       if (var != DBUS_TYPE_UINT16)
+                               return -EINVAL;
+                       dbus_message_iter_get_basic(&value, &chrc->mtu);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return 0;
+}
+
+static DBusMessage *chrc_create_pipe(struct chrc *chrc, DBusMessage *msg)
+{
+       int pipefd[2];
+       struct io *io;
+       bool dir;
+       DBusMessage *reply;
+
+       if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+                                                       strerror(errno));
+
+       dir = dbus_message_has_member(msg, "AcquireWrite");
+
+       io = pipe_io_new(pipefd[!dir], chrc);
+       if (!io) {
+               close(pipefd[0]);
+               close(pipefd[1]);
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
+                                                       strerror(errno));
+       }
+
+       reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+                                       DBUS_TYPE_UINT16, &chrc->mtu,
+                                       DBUS_TYPE_INVALID);
+
+       close(pipefd[dir]);
+
+       if (dir)
+               chrc->write_io = io;
+       else
+               chrc->notify_io = io;
+
+       bt_shell_printf("[" COLORED_CHG "] Attribute %s %s pipe acquired\n",
+                                       chrc->path, dir ? "Write" : "Notify");
+
+       return reply;
+}
+
+static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+       DBusMessageIter iter;
+       DBusMessage *reply;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (chrc->write_io)
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.NotPermitted",
+                                       NULL);
+
+       if (parse_options(&iter, chrc))
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.InvalidArguments",
+                                       NULL);
+
+       reply = chrc_create_pipe(chrc, msg);
+
+       if (chrc->write_io)
+               g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+                                                       "WriteAcquired");
+
+       return reply;
+}
+
+static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+       DBusMessageIter iter;
+       DBusMessage *reply;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (chrc->notify_io)
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.NotPermitted",
+                                       NULL);
+
+       if (parse_options(&iter, chrc))
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.InvalidArguments",
+                                       NULL);
+
+       reply = chrc_create_pipe(chrc, msg);
+
+       if (chrc->notify_io)
+               g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+                                                       "NotifyAcquired");
+
+       return reply;
+}
+
+static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       if (!chrc->notifying)
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       chrc->notifying = true;
+       bt_shell_printf("[" COLORED_CHG "] Attribute %s notifications enabled",
+                                                       chrc->path);
+       g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+                                                       "Notifying");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       if (chrc->notifying)
+               return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+       chrc->notifying = false;
+       bt_shell_printf("[" COLORED_CHG "] Attribute %s notifications disabled",
+                                                       chrc->path);
+       g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
+                                                       "Notifying");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *chrc_confirm(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       bt_shell_printf("Attribute %s indication confirm received", chrc->path);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable chrc_methods[] = {
+       { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
+                                       GDBUS_ARGS({ "value", "ay" }),
+                                       chrc_read_value) },
+       { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
+                                               { "options", "a{sv}" }),
+                                       NULL, chrc_write_value) },
+       { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
+                                       NULL, chrc_acquire_write) },
+       { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }),
+                                       NULL, chrc_acquire_notify) },
+       { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
+       { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
+       { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
+       { }
+};
+
+static uint8_t *str2bytearray(char *arg, int *val_len)
+{
+       uint8_t value[512];
+       char *entry;
+       unsigned int i;
+
+       for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
+               long int val;
+               char *endptr = NULL;
+
+               if (*entry == '\0')
+                       continue;
+
+               if (i >= G_N_ELEMENTS(value)) {
+                       bt_shell_printf("Too much data\n");
+                       return NULL;
+               }
+
+               val = strtol(entry, &endptr, 0);
+               if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+                       bt_shell_printf("Invalid value at index %d\n", i);
+                       return NULL;
+               }
+
+               value[i] = val;
+       }
+
+       *val_len = i;
+
+       return g_memdup(value, i);
+}
+
+static void chrc_set_value(const char *input, void *user_data)
+{
+       struct chrc *chrc = user_data;
+
+       g_free(chrc->value);
+
+       chrc->value = str2bytearray((char *) input, &chrc->value_len);
+}
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
+                                       int argc, char *argv[])
+{
+       struct service *service;
+       struct chrc *chrc;
+
+       if (!local_services) {
+               bt_shell_printf("No service registered\n");
+               return;
+       }
+
+       service = g_list_last(local_services)->data;
+
+       chrc = g_new0(struct chrc, 1);
+       chrc->service = service;
+       chrc->uuid = g_strdup(argv[1]);
+       chrc->path = g_strdup_printf("%s/chrc%p", service->path, chrc);
+       chrc->flags = g_strsplit(argv[1], ",", -1);
+
+       if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
+                                       chrc_methods, NULL, chrc_properties,
+                                       chrc, chrc_free) == FALSE) {
+               bt_shell_printf("Failed to register characteristic object\n");
+               chrc_free(chrc);
+               return;
+       }
+
+       service->chrcs = g_list_append(service->chrcs, chrc);
+
+       print_chrc(chrc, COLORED_NEW);
+
+       bt_shell_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
+}
+
+static struct chrc *chrc_find(const char *pattern)
+{
+       GList *l, *lc;
+       struct service *service;
+       struct chrc *chrc;
+
+       for (l = local_services; l; l = g_list_next(l)) {
+               service = l->data;
+
+               for (lc = service->chrcs; lc; lc =  g_list_next(lc)) {
+                       chrc = lc->data;
+
+                       /* match object path */
+                       if (!strcmp(chrc->path, pattern))
+                               return chrc;
+
+                       /* match UUID */
+                       if (!strcmp(chrc->uuid, pattern))
+                               return chrc;
+               }
+       }
+
+       return NULL;
+}
+
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[])
+{
+       struct chrc *chrc;
+
+       chrc = chrc_find(argv[1]);
+       if (!chrc) {
+               bt_shell_printf("Failed to unregister characteristic object\n");
+               return;
+       }
+
+       chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc);
+
+       chrc_unregister(chrc);
+}
+
+static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct desc *desc = user_data;
+
+       return read_value(msg, desc->value, desc->value_len);
+}
+
+static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct desc *desc = user_data;
+       DBusMessageIter iter;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (parse_value_arg(&iter, &desc->value, &desc->value_len))
+               return g_dbus_create_error(msg,
+                                       "org.bluez.Error.InvalidArguments",
+                                       NULL);
+
+       bt_shell_printf("[" COLORED_CHG "] Attribute %s written" , desc->path);
+
+       g_dbus_emit_property_changed(conn, desc->path, CHRC_INTERFACE, "Value");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+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 desc_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct desc *desc = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
+
+       return TRUE;
+}
+
+static gboolean desc_get_chrc(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct desc *desc = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
+                                               &desc->chrc->path);
+
+       return TRUE;
+}
+
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct desc *desc = data;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+
+       if (desc->value)
+               dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                                       &desc->value,
+                                                       desc->value_len);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static gboolean desc_get_flags(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct desc *desc = data;
+       int i;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
+
+       for (i = 0; desc->flags[i]; i++)
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+                                                       &desc->flags[i]);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable desc_properties[] = {
+       { "UUID", "s", desc_get_uuid, NULL, NULL },
+       { "Characteristic", "o", desc_get_chrc, NULL, NULL },
+       { "Value", "ay", desc_get_value, NULL, NULL },
+       { "Flags", "as", desc_get_flags, NULL, NULL },
+       { }
+};
+
+static void desc_set_value(const char *input, void *user_data)
+{
+       struct desc *desc = user_data;
+
+       g_free(desc->value);
+
+       desc->value = str2bytearray((char *) input, &desc->value_len);
+}
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[])
+{
+       struct service *service;
+       struct desc *desc;
+
+       if (!local_services) {
+               bt_shell_printf("No service registered\n");
+               return;
+       }
+
+       service = g_list_last(local_services)->data;
+
+       if (!service->chrcs) {
+               bt_shell_printf("No characteristic registered\n");
+               return;
+       }
+
+       desc = g_new0(struct desc, 1);
+       desc->chrc = g_list_last(service->chrcs)->data;
+       desc->uuid = g_strdup(argv[1]);
+       desc->path = g_strdup_printf("%s/desc%p", desc->chrc->path, desc);
+       desc->flags = g_strsplit(argv[1], ",", -1);
+
+       if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE,
+                                       desc_methods, NULL, desc_properties,
+                                       desc, desc_free) == FALSE) {
+               bt_shell_printf("Failed to register descriptor object\n");
+               desc_free(desc);
+               return;
+       }
+
+       desc->chrc->descs = g_list_append(desc->chrc->descs, desc);
+
+       print_desc(desc, COLORED_NEW);
+
+       bt_shell_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
+}
+
+static struct desc *desc_find(const char *pattern)
+{
+       GList *l, *lc, *ld;
+       struct service *service;
+       struct chrc *chrc;
+       struct desc *desc;
+
+       for (l = local_services; l; l = g_list_next(l)) {
+               service = l->data;
+
+               for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
+                       chrc = lc->data;
+
+                       for (ld = chrc->descs; ld; ld = g_list_next(ld)) {
+                               desc = ld->data;
+
+                               /* match object path */
+                               if (!strcmp(desc->path, pattern))
+                                       return desc;
+
+                               /* match UUID */
+                               if (!strcmp(desc->uuid, pattern))
+                                       return desc;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[])
+{
+       struct desc *desc;
+
+       desc = desc_find(argv[1]);
+       if (!desc) {
+               bt_shell_printf("Failed to unregister descriptor object\n");
+               return;
+       }
+
+       desc->chrc->descs = g_list_remove(desc->chrc->descs, desc);
+
+       desc_unregister(desc);
+}
index 689bb4d..f4c36b8 100755 (executable)
@@ -31,16 +31,37 @@ void gatt_add_descriptor(GDBusProxy *proxy);
 void gatt_remove_descriptor(GDBusProxy *proxy);
 
 void gatt_list_attributes(const char *device);
-GDBusProxy *gatt_select_attribute(const char *path);
+GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *path);
 char *gatt_attribute_generator(const char *text, int state);
 
 void gatt_read_attribute(GDBusProxy *proxy);
 void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
 void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
 
+void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
+void gatt_release_write(GDBusProxy *proxy, const char *arg);
+
+void gatt_acquire_notify(GDBusProxy *proxy, const char *arg);
+void gatt_release_notify(GDBusProxy *proxy, const char *arg);
+
 void gatt_add_manager(GDBusProxy *proxy);
 void gatt_remove_manager(GDBusProxy *proxy);
 
-void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
-                                                               wordexp_t *w);
-void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy);
+void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy);
+
+void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+
+void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+
+void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
+void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
+                                               int argc, char *argv[]);
index bbf4cd3..76842b1 100755 (executable)
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdbool.h>
-#include <signal.h>
-#include <sys/signalfd.h>
 #include <wordexp.h>
 
-#include <readline/readline.h>
-#include <readline/history.h>
 #include <glib.h>
 
+#include "src/shared/shell.h"
 #include "src/shared/util.h"
 #include "gdbus/gdbus.h"
-#include "monitor/uuid.h"
 #include "agent.h"
-#include "display.h"
 #include "gatt.h"
 #include "advertising.h"
 
@@ -54,7 +49,6 @@
 #define PROMPT_ON      COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
 #define PROMPT_OFF     "Waiting to connect to bluetoothd..."
 
-static GMainLoop *main_loop;
 static DBusConnection *dbus_conn;
 
 static GDBusProxy *agent_manager;
@@ -62,18 +56,22 @@ static char *auto_register_agent = NULL;
 
 struct adapter {
        GDBusProxy *proxy;
+       GDBusProxy *ad_proxy;
        GList *devices;
 };
 
 static struct adapter *default_ctrl;
 static GDBusProxy *default_dev;
 static GDBusProxy *default_attr;
-static GDBusProxy *ad_manager;
 static GList *ctrl_list;
 
-static guint input = 0;
+static const char *mode_arguments[] = {
+       "on",
+       "off",
+       NULL
+};
 
-static const char * const agent_arguments[] = {
+static const char *agent_arguments[] = {
        "on",
        "off",
        "DisplayOnly",
@@ -84,7 +82,7 @@ static const char * const agent_arguments[] = {
        NULL
 };
 
-static const char * const ad_arguments[] = {
+static const char *ad_arguments[] = {
        "on",
        "off",
        "peripheral",
@@ -97,57 +95,21 @@ static void proxy_leak(gpointer data)
        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)
+static void 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;
+       bt_shell_attach(fileno(stdin));
 }
 
 static void connect_handler(DBusConnection *connection, void *user_data)
 {
-       rl_set_prompt(PROMPT_ON);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
+       bt_shell_set_prompt(PROMPT_ON);
 }
 
 static void disconnect_handler(DBusConnection *connection, void *user_data)
 {
-       if (input > 0) {
-               g_source_remove(input);
-               input = 0;
-       }
+       bt_shell_detach();
 
-       rl_set_prompt(PROMPT_OFF);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
+       bt_shell_set_prompt(PROMPT_OFF);
 
        g_list_free_full(ctrl_list, proxy_leak);
        ctrl_list = NULL;
@@ -170,7 +132,7 @@ static void print_adapter(GDBusProxy *proxy, const char *description)
        else
                name = "<unknown>";
 
-       rl_printf("%s%s%sController %s %s %s\n",
+       bt_shell_printf("%s%s%sController %s %s %s\n",
                                description ? "[" : "",
                                description ? : "",
                                description ? "] " : "",
@@ -196,13 +158,79 @@ static void print_device(GDBusProxy *proxy, const char *description)
        else
                name = "<unknown>";
 
-       rl_printf("%s%s%sDevice %s %s\n",
+       bt_shell_printf("%s%s%sDevice %s %s\n",
                                description ? "[" : "",
                                description ? : "",
                                description ? "] " : "",
                                address, name);
 }
 
+static void print_fixed_iter(const char *label, const char *name,
+                                               DBusMessageIter *iter)
+{
+       dbus_bool_t *valbool;
+       dbus_uint32_t *valu32;
+       dbus_uint16_t *valu16;
+       dbus_int16_t *vals16;
+       unsigned char *byte;
+       int len;
+
+       switch (dbus_message_iter_get_arg_type(iter)) {
+       case DBUS_TYPE_BOOLEAN:
+               dbus_message_iter_get_fixed_array(iter, &valbool, &len);
+
+               if (len <= 0)
+                       return;
+
+               bt_shell_printf("%s%s:\n", label, name);
+               bt_shell_hexdump((void *)valbool, len * sizeof(*valbool));
+
+               break;
+       case DBUS_TYPE_UINT32:
+               dbus_message_iter_get_fixed_array(iter, &valu32, &len);
+
+               if (len <= 0)
+                       return;
+
+               bt_shell_printf("%s%s:\n", label, name);
+               bt_shell_hexdump((void *)valu32, len * sizeof(*valu32));
+
+               break;
+       case DBUS_TYPE_UINT16:
+               dbus_message_iter_get_fixed_array(iter, &valu16, &len);
+
+               if (len <= 0)
+                       return;
+
+               bt_shell_printf("%s%s:\n", label, name);
+               bt_shell_hexdump((void *)valu16, len * sizeof(*valu16));
+
+               break;
+       case DBUS_TYPE_INT16:
+               dbus_message_iter_get_fixed_array(iter, &vals16, &len);
+
+               if (len <= 0)
+                       return;
+
+               bt_shell_printf("%s%s:\n", label, name);
+               bt_shell_hexdump((void *)vals16, len * sizeof(*vals16));
+
+               break;
+       case DBUS_TYPE_BYTE:
+               dbus_message_iter_get_fixed_array(iter, &byte, &len);
+
+               if (len <= 0)
+                       return;
+
+               bt_shell_printf("%s%s:\n", label, name);
+               bt_shell_hexdump((void *)byte, len * sizeof(*byte));
+
+               break;
+       default:
+               return;
+       };
+}
+
 static void print_iter(const char *label, const char *name,
                                                DBusMessageIter *iter)
 {
@@ -216,39 +244,39 @@ static void print_iter(const char *label, const char *name,
        char *entry;
 
        if (iter == NULL) {
-               rl_printf("%s%s is nil\n", label, name);
+               bt_shell_printf("%s%s is nil\n", label, name);
                return;
        }
 
        switch (dbus_message_iter_get_arg_type(iter)) {
        case DBUS_TYPE_INVALID:
-               rl_printf("%s%s is invalid\n", label, name);
+               bt_shell_printf("%s%s is invalid\n", label, name);
                break;
        case DBUS_TYPE_STRING:
        case DBUS_TYPE_OBJECT_PATH:
                dbus_message_iter_get_basic(iter, &valstr);
-               rl_printf("%s%s: %s\n", label, name, valstr);
+               bt_shell_printf("%s%s: %s\n", label, name, valstr);
                break;
        case DBUS_TYPE_BOOLEAN:
                dbus_message_iter_get_basic(iter, &valbool);
-               rl_printf("%s%s: %s\n", label, name,
+               bt_shell_printf("%s%s: %s\n", label, name,
                                        valbool == TRUE ? "yes" : "no");
                break;
        case DBUS_TYPE_UINT32:
                dbus_message_iter_get_basic(iter, &valu32);
-               rl_printf("%s%s: 0x%06x\n", label, name, valu32);
+               bt_shell_printf("%s%s: 0x%08x\n", label, name, valu32);
                break;
        case DBUS_TYPE_UINT16:
                dbus_message_iter_get_basic(iter, &valu16);
-               rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+               bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
                break;
        case DBUS_TYPE_INT16:
                dbus_message_iter_get_basic(iter, &vals16);
-               rl_printf("%s%s: %d\n", label, name, vals16);
+               bt_shell_printf("%s%s: %d\n", label, name, vals16);
                break;
        case DBUS_TYPE_BYTE:
                dbus_message_iter_get_basic(iter, &byte);
-               rl_printf("%s%s: 0x%02x\n", label, name, byte);
+               bt_shell_printf("%s%s: 0x%02x\n", label, name, byte);
                break;
        case DBUS_TYPE_VARIANT:
                dbus_message_iter_recurse(iter, &subiter);
@@ -256,6 +284,13 @@ static void print_iter(const char *label, const char *name,
                break;
        case DBUS_TYPE_ARRAY:
                dbus_message_iter_recurse(iter, &subiter);
+
+               if (dbus_type_is_fixed(
+                               dbus_message_iter_get_arg_type(&subiter))) {
+                       print_fixed_iter(label, name, &subiter);
+                       break;
+               }
+
                while (dbus_message_iter_get_arg_type(&subiter) !=
                                                        DBUS_TYPE_INVALID) {
                        print_iter(label, name, &subiter);
@@ -274,7 +309,7 @@ static void print_iter(const char *label, const char *name,
                g_free(entry);
                break;
        default:
-               rl_printf("%s%s has unsupported type\n", label, name);
+               bt_shell_printf("%s%s has unsupported type\n", label, name);
                break;
        }
 }
@@ -289,6 +324,32 @@ static void print_property(GDBusProxy *proxy, const char *name)
        print_iter("\t", name, &iter);
 }
 
+static void print_uuid(const char *uuid)
+{
+       const char *text;
+
+       text = bt_uuidstr_to_str(uuid);
+       if (text) {
+               char str[26];
+               unsigned int n;
+
+               str[sizeof(str) - 1] = '\0';
+
+               n = snprintf(str, sizeof(str), "%s", text);
+               if (n > sizeof(str) - 1) {
+                       str[sizeof(str) - 2] = '.';
+                       str[sizeof(str) - 3] = '.';
+                       if (str[sizeof(str) - 4] == ' ')
+                               str[sizeof(str) - 4] = '.';
+
+                       n = sizeof(str) - 1;
+               }
+
+               bt_shell_printf("\tUUID: %s%*c(%s)\n", str, 26 - n, ' ', uuid);
+       } else
+               bt_shell_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+}
+
 static void print_uuids(GDBusProxy *proxy)
 {
        DBusMessageIter iter, value;
@@ -299,31 +360,11 @@ static void print_uuids(GDBusProxy *proxy)
        dbus_message_iter_recurse(&iter, &value);
 
        while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
-               const char *uuid, *text;
+               const char *uuid;
 
                dbus_message_iter_get_basic(&value, &uuid);
 
-               text = uuidstr_to_str(uuid);
-               if (text) {
-                       char str[26];
-                       unsigned int n;
-
-                       str[sizeof(str) - 1] = '\0';
-
-                       n = snprintf(str, sizeof(str), "%s", text);
-                       if (n > sizeof(str) - 1) {
-                               str[sizeof(str) - 2] = '.';
-                               str[sizeof(str) - 3] = '.';
-                               if (str[sizeof(str) - 4] == ' ')
-                                       str[sizeof(str) - 4] = '.';
-
-                               n = sizeof(str) - 1;
-                       }
-
-                       rl_printf("\tUUID: %s%*c(%s)\n",
-                                               str, 26 - n, ' ', uuid);
-               } else
-                       rl_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+               print_uuid(uuid);
 
                dbus_message_iter_next(&value);
        }
@@ -414,9 +455,7 @@ static void set_default_device(GDBusProxy *proxy, const char *attribute)
                                attribute ? attribute + strlen(path) : "");
 
 done:
-       rl_set_prompt(desc ? desc : PROMPT_ON);
-       printf("\r");
-       rl_on_new_line();
+       bt_shell_set_prompt(desc ? desc : PROMPT_ON);
        g_free(desc);
 }
 
@@ -446,19 +485,42 @@ static void device_added(GDBusProxy *proxy)
        }
 }
 
-static void adapter_added(GDBusProxy *proxy)
+static struct adapter *find_ctrl(GList *source, const char *path);
+
+static struct adapter *adapter_new(GDBusProxy *proxy)
 {
        struct adapter *adapter = g_malloc0(sizeof(struct adapter));
 
-       adapter->proxy = proxy;
        ctrl_list = g_list_append(ctrl_list, adapter);
 
        if (!default_ctrl)
                default_ctrl = adapter;
 
+       return adapter;
+}
+
+static void adapter_added(GDBusProxy *proxy)
+{
+       struct adapter *adapter;
+       adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+       if (!adapter)
+               adapter = adapter_new(proxy);
+
+       adapter->proxy = proxy;
+
        print_adapter(proxy, COLORED_NEW);
 }
 
+static void ad_manager_added(GDBusProxy *proxy)
+{
+       struct adapter *adapter;
+       adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+       if (!adapter)
+               adapter = adapter_new(proxy);
+
+       adapter->ad_proxy = proxy;
+}
+
 static void proxy_added(GDBusProxy *proxy, void *user_data)
 {
        const char *interface;
@@ -487,7 +549,7 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
        } else if (!strcmp(interface, "org.bluez.GattManager1")) {
                gatt_add_manager(proxy);
        } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
-               ad_manager = proxy;
+               ad_manager_added(proxy);
        }
 }
 
@@ -576,17 +638,29 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
        } else if (!strcmp(interface, "org.bluez.GattManager1")) {
                gatt_remove_manager(proxy);
        } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
-               if (ad_manager == proxy) {
-                       agent_manager = NULL;
-                       ad_unregister(dbus_conn, NULL);
-               }
+               ad_unregister(dbus_conn, NULL);
+       }
+}
+
+static struct adapter *find_ctrl(GList *source, const char *path)
+{
+       GList *list;
+
+       for (list = g_list_first(source); list; list = g_list_next(list)) {
+               struct adapter *adapter = list->data;
+
+               if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), path))
+                       return adapter;
        }
+
+       return NULL;
 }
 
 static void property_changed(GDBusProxy *proxy, const char *name,
                                        DBusMessageIter *iter, void *user_data)
 {
        const char *interface;
+       struct adapter *ctrl;
 
        interface = g_dbus_proxy_get_interface(proxy);
 
@@ -637,6 +711,27 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 
                print_iter(str, name, iter);
                g_free(str);
+       } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
+               DBusMessageIter addr_iter;
+               char *str;
+
+               ctrl = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
+               if (!ctrl)
+                       return;
+
+               if (g_dbus_proxy_get_property(ctrl->proxy, "Address",
+                                               &addr_iter) == TRUE) {
+                       const char *address;
+
+                       dbus_message_iter_get_basic(&addr_iter, &address);
+                       str = g_strdup_printf("[" COLORED_CHG
+                                               "] Controller %s ",
+                                               address);
+               } else
+                       str = g_strdup("");
+
+               print_iter(str, name, iter);
+               g_free(str);
        } else if (proxy == default_attr) {
                char *str;
 
@@ -651,7 +746,7 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 static void message_handler(DBusConnection *connection,
                                        DBusMessage *message, void *user_data)
 {
-       rl_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
+       bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
                                        dbus_message_get_member(message));
 }
 
@@ -670,7 +765,7 @@ static struct adapter *find_ctrl_by_address(GList *source, const char *address)
 
                dbus_message_iter_get_basic(&iter, &str);
 
-               if (!strcmp(str, address))
+               if (!strcasecmp(str, address))
                        return adapter;
        }
 
@@ -691,7 +786,7 @@ static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
 
                dbus_message_iter_get_basic(&iter, &str);
 
-               if (!strcmp(str, address))
+               if (!strcasecmp(str, address))
                        return proxy;
        }
 
@@ -701,68 +796,44 @@ static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
 static gboolean check_default_ctrl(void)
 {
        if (!default_ctrl) {
-               rl_printf("No default controller available\n");
+               bt_shell_printf("No default controller available\n");
                return FALSE;
        }
 
        return TRUE;
 }
 
-static gboolean parse_argument_on_off(const char *arg, dbus_bool_t *value)
-{
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing on/off argument\n");
-               return FALSE;
-       }
-
-       if (!strcmp(arg, "on") || !strcmp(arg, "yes")) {
-               *value = TRUE;
-               return TRUE;
-       }
-
-       if (!strcmp(arg, "off") || !strcmp(arg, "no")) {
-               *value = FALSE;
-               return TRUE;
-       }
-
-       rl_printf("Invalid argument %s\n", arg);
-       return FALSE;
-}
-
-static gboolean parse_argument_agent(const char *arg, dbus_bool_t *value,
-                                                       const char **capability)
+static gboolean parse_argument(int argc, char *argv[], const char **arg_table,
+                                       const char *msg, dbus_bool_t *value,
+                                       const char **option)
 {
-       const char * const *opt;
-
-       if (arg == NULL || strlen(arg) == 0) {
-               rl_printf("Missing on/off/capability argument\n");
-               return FALSE;
-       }
+       const char **opt;
 
-       if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
+       if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
                *value = TRUE;
-               *capability = "";
+               if (option)
+                       *option = "";
                return TRUE;
        }
 
-       if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
+       if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
                *value = FALSE;
                return TRUE;
        }
 
-       for (opt = agent_arguments; *opt; opt++) {
-               if (strcmp(arg, *opt) == 0) {
+       for (opt = arg_table; opt && *opt; opt++) {
+               if (strcmp(argv[1], *opt) == 0) {
                        *value = TRUE;
-                       *capability = *opt;
+                       *option = *opt;
                        return TRUE;
                }
        }
 
-       rl_printf("Invalid argument %s\n", arg);
+       bt_shell_printf("Invalid argument %s\n", argv[1]);
        return FALSE;
 }
 
-static void cmd_list(const char *arg)
+static void cmd_list(int argc, char *argv[])
 {
        GList *list;
 
@@ -772,22 +843,23 @@ static void cmd_list(const char *arg)
        }
 }
 
-static void cmd_show(const char *arg)
+static void cmd_show(int argc, char *argv[])
 {
        struct adapter *adapter;
        GDBusProxy *proxy;
        DBusMessageIter iter;
        const char *address;
 
-       if (!arg || !strlen(arg)) {
+       if (argc < 2 || !strlen(argv[1])) {
                if (check_default_ctrl() == FALSE)
                        return;
 
                proxy = default_ctrl->proxy;
        } else {
-               adapter = find_ctrl_by_address(ctrl_list, arg);
+               adapter = find_ctrl_by_address(ctrl_list, argv[1]);
                if (!adapter) {
-                       rl_printf("Controller %s not available\n", arg);
+                       bt_shell_printf("Controller %s not available\n",
+                                                               argv[1]);
                        return;
                }
                proxy = adapter->proxy;
@@ -797,7 +869,16 @@ static void cmd_show(const char *arg)
                return;
 
        dbus_message_iter_get_basic(&iter, &address);
-       rl_printf("Controller %s\n", address);
+
+       if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) {
+               const char *type;
+
+               dbus_message_iter_get_basic(&iter, &type);
+
+               bt_shell_printf("Controller %s (%s)\n", address, type);
+       } else {
+               bt_shell_printf("Controller %s\n", address);
+       }
 
        print_property(proxy, "Name");
        print_property(proxy, "Alias");
@@ -813,18 +894,13 @@ static void cmd_show(const char *arg)
 #endif
 }
 
-static void cmd_select(const char *arg)
+static void cmd_select(int argc, char *argv[])
 {
        struct adapter *adapter;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing controller address argument\n");
-               return;
-       }
-
-       adapter = find_ctrl_by_address(ctrl_list, arg);
+       adapter = find_ctrl_by_address(ctrl_list, argv[1]);
        if (!adapter) {
-               rl_printf("Controller %s not available\n", arg);
+               bt_shell_printf("Controller %s not available\n", argv[1]);
                return;
        }
 
@@ -835,7 +911,7 @@ static void cmd_select(const char *arg)
        print_adapter(adapter->proxy, NULL);
 }
 
-static void cmd_devices(const char *arg)
+static void cmd_devices(int argc, char *argv[])
 {
        GList *ll;
 
@@ -849,7 +925,7 @@ static void cmd_devices(const char *arg)
        }
 }
 
-static void cmd_paired_devices(const char *arg)
+static void cmd_paired_devices(int argc, char *argv[])
 {
        GList *ll;
 
@@ -878,24 +954,19 @@ static void generic_callback(const DBusError *error, void *user_data)
        char *str = user_data;
 
        if (dbus_error_is_set(error))
-               rl_printf("Failed to set %s: %s\n", str, error->name);
+               bt_shell_printf("Failed to set %s: %s\n", str, error->name);
        else
-               rl_printf("Changing %s succeeded\n", str);
+               bt_shell_printf("Changing %s succeeded\n", str);
 }
 
-static void cmd_system_alias(const char *arg)
+static void cmd_system_alias(int argc, char *argv[])
 {
        char *name;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing name argument\n");
-               return;
-       }
-
        if (check_default_ctrl() == FALSE)
                return;
 
-       name = g_strdup(arg);
+       name = g_strdup(argv[1]);
 
        if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
                                        DBUS_TYPE_STRING, &name,
@@ -905,7 +976,7 @@ static void cmd_system_alias(const char *arg)
        g_free(name);
 }
 
-static void cmd_reset_alias(const char *arg)
+static void cmd_reset_alias(int argc, char *argv[])
 {
        char *name;
 
@@ -922,12 +993,12 @@ static void cmd_reset_alias(const char *arg)
        g_free(name);
 }
 
-static void cmd_power(const char *arg)
+static void cmd_power(int argc, char *argv[])
 {
        dbus_bool_t powered;
        char *str;
 
-       if (parse_argument_on_off(arg, &powered) == FALSE)
+       if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
                return;
 
        if (check_default_ctrl() == FALSE)
@@ -943,12 +1014,12 @@ static void cmd_power(const char *arg)
        g_free(str);
 }
 
-static void cmd_pairable(const char *arg)
+static void cmd_pairable(int argc, char *argv[])
 {
        dbus_bool_t pairable;
        char *str;
 
-       if (parse_argument_on_off(arg, &pairable) == FALSE)
+       if (!parse_argument(argc, argv, NULL, NULL, &pairable, NULL))
                return;
 
        if (check_default_ctrl() == FALSE)
@@ -964,12 +1035,12 @@ static void cmd_pairable(const char *arg)
        g_free(str);
 }
 
-static void cmd_discoverable(const char *arg)
+static void cmd_discoverable(int argc, char *argv[])
 {
        dbus_bool_t discoverable;
        char *str;
 
-       if (parse_argument_on_off(arg, &discoverable) == FALSE)
+       if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL))
                return;
 
        if (check_default_ctrl() == FALSE)
@@ -986,12 +1057,13 @@ static void cmd_discoverable(const char *arg)
        g_free(str);
 }
 
-static void cmd_agent(const char *arg)
+static void cmd_agent(int argc, char *argv[])
 {
        dbus_bool_t enable;
        const char *capability;
 
-       if (parse_argument_agent(arg, &enable, &capability) == FALSE)
+       if (!parse_argument(argc, argv, agent_arguments, "capability",
+                                               &enable, &capability))
                return;
 
        if (enable == TRUE) {
@@ -1002,7 +1074,7 @@ static void cmd_agent(const char *arg)
                        agent_register(dbus_conn, agent_manager,
                                                auto_register_agent);
                else
-                       rl_printf("Agent registration enabled\n");
+                       bt_shell_printf("Agent registration enabled\n");
        } else {
                g_free(auto_register_agent);
                auto_register_agent = NULL;
@@ -1010,11 +1082,11 @@ static void cmd_agent(const char *arg)
                if (agent_manager)
                        agent_unregister(dbus_conn, agent_manager);
                else
-                       rl_printf("Agent registration disabled\n");
+                       bt_shell_printf("Agent registration disabled\n");
        }
 }
 
-static void cmd_default_agent(const char *arg)
+static void cmd_default_agent(int argc, char *argv[])
 {
        agent_default(dbus_conn, agent_manager);
 }
@@ -1027,38 +1099,13 @@ static void start_discovery_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to %s discovery: %s\n",
+               bt_shell_printf("Failed to %s discovery: %s\n",
                                enable == TRUE ? "start" : "stop", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
-}
-
-static void cmd_scan(const char *arg)
-{
-       dbus_bool_t enable;
-       const char *method;
-
-       if (parse_argument_on_off(arg, &enable) == FALSE)
-               return;
-
-       if (check_default_ctrl() == FALSE)
-               return;
-
-       if (enable == TRUE)
-               method = "StartDiscovery";
-       else
-               method = "StopDiscovery";
-
-       if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
-                               NULL, start_discovery_reply,
-                               GUINT_TO_POINTER(enable), NULL) == FALSE) {
-               rl_printf("Failed to %s discovery\n",
-                                       enable == TRUE ? "start" : "stop");
-               return;
-       }
+       bt_shell_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
 }
 
 static void append_variant(DBusMessageIter *iter, int type, void *val)
@@ -1150,12 +1197,18 @@ static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
 
 #define        DISTANCE_VAL_INVALID    0x7FFF
 
-struct set_discovery_filter_args {
+static struct set_discovery_filter_args {
        char *transport;
        dbus_uint16_t rssi;
        dbus_int16_t pathloss;
        char **uuids;
        size_t uuids_len;
+       dbus_bool_t duplicate;
+       bool set;
+} filter = {
+       .rssi = DISTANCE_VAL_INVALID,
+       .pathloss = DISTANCE_VAL_INVALID,
+       .set = true,
 };
 
 static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
@@ -1183,6 +1236,10 @@ static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
                dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING,
                                                &args->transport);
 
+       if (args->duplicate)
+               dict_append_entry(&dict, "DuplicateData", DBUS_TYPE_BOOLEAN,
+                                               &args->duplicate);
+
        dbus_message_iter_close_container(iter, &dict);
 }
 
@@ -1193,163 +1250,270 @@ static void set_discovery_filter_reply(DBusMessage *message, void *user_data)
 
        dbus_error_init(&error);
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("SetDiscoveryFilter failed: %s\n", error.name);
+               bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("SetDiscoveryFilter success\n");
+       filter.set = true;
+
+       bt_shell_printf("SetDiscoveryFilter success\n");
 }
 
-static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
-static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-static char **filtered_scan_uuids;
-static size_t filtered_scan_uuids_len;
-static char *filtered_scan_transport;
+static void set_discovery_filter(void)
+{
+       if (check_default_ctrl() == FALSE || filter.set)
+               return;
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
+               set_discovery_filter_setup, set_discovery_filter_reply,
+               &filter, NULL) == FALSE) {
+               bt_shell_printf("Failed to set discovery filter\n");
+               return;
+       }
+
+       filter.set = true;
+}
 
-static void cmd_set_scan_filter_commit(void)
+static void cmd_scan(int argc, char *argv[])
 {
-       struct set_discovery_filter_args args;
+       dbus_bool_t enable;
+       const char *method;
 
-       args.uuids = NULL;
-       args.pathloss = filtered_scan_pathloss;
-       args.rssi = filtered_scan_rssi;
-       args.transport = filtered_scan_transport;
-       args.uuids = filtered_scan_uuids;
-       args.uuids_len = filtered_scan_uuids_len;
+       if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
+               return;
 
        if (check_default_ctrl() == FALSE)
                return;
 
-       if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
-               set_discovery_filter_setup, set_discovery_filter_reply,
-               &args, NULL) == FALSE) {
-               rl_printf("Failed to set discovery filter\n");
+       if (enable == TRUE) {
+               set_discovery_filter();
+               method = "StartDiscovery";
+       } else
+               method = "StopDiscovery";
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
+                               NULL, start_discovery_reply,
+                               GUINT_TO_POINTER(enable), NULL) == FALSE) {
+               bt_shell_printf("Failed to %s discovery\n",
+                                       enable == TRUE ? "start" : "stop");
                return;
        }
 }
 
-static void cmd_set_scan_filter_uuids(const char *arg)
+static void cmd_scan_filter_uuids(int argc, char *argv[])
 {
-       g_strfreev(filtered_scan_uuids);
-       filtered_scan_uuids = NULL;
-       filtered_scan_uuids_len = 0;
+       if (argc < 2 || !strlen(argv[1])) {
+               char **uuid;
+
+               for (uuid = filter.uuids; uuid && *uuid; uuid++)
+                       print_uuid(*uuid);
+
+               return;
+       }
+
+       g_strfreev(filter.uuids);
+       filter.uuids = NULL;
+       filter.uuids_len = 0;
 
-       if (!arg || !strlen(arg))
+       if (!strcmp(argv[1], "all"))
                goto commit;
 
-       filtered_scan_uuids = g_strsplit(arg, " ", -1);
-       if (!filtered_scan_uuids) {
-               rl_printf("Failed to parse input\n");
+       filter.uuids = g_strdupv(&argv[1]);
+       if (!filter.uuids) {
+               bt_shell_printf("Failed to parse input\n");
                return;
        }
 
-       filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids);
+       filter.uuids_len = g_strv_length(filter.uuids);
 
 commit:
-       cmd_set_scan_filter_commit();
+       filter.set = false;
 }
 
-static void cmd_set_scan_filter_rssi(const char *arg)
+static void cmd_scan_filter_rssi(int argc, char *argv[])
 {
-       filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+       if (argc < 2 || !strlen(argv[1])) {
+               if (filter.rssi != DISTANCE_VAL_INVALID)
+                       bt_shell_printf("RSSI: %d\n", filter.rssi);
+               return;
+       }
 
-       if (!arg || !strlen(arg))
-               filtered_scan_rssi = DISTANCE_VAL_INVALID;
-       else
-               filtered_scan_rssi = atoi(arg);
+       filter.pathloss = DISTANCE_VAL_INVALID;
+       filter.rssi = atoi(argv[1]);
 
-       cmd_set_scan_filter_commit();
+       filter.set = false;
 }
 
-static void cmd_set_scan_filter_pathloss(const char *arg)
+static void cmd_scan_filter_pathloss(int argc, char *argv[])
 {
-       filtered_scan_rssi = DISTANCE_VAL_INVALID;
+       if (argc < 2 || !strlen(argv[1])) {
+               if (filter.pathloss != DISTANCE_VAL_INVALID)
+                       bt_shell_printf("Pathloss: %d\n",
+                                               filter.pathloss);
+               return;
+       }
 
-       if (!arg || !strlen(arg))
-               filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-       else
-               filtered_scan_pathloss = atoi(arg);
+       filter.rssi = DISTANCE_VAL_INVALID;
+       filter.pathloss = atoi(argv[1]);
 
-       cmd_set_scan_filter_commit();
+       filter.set = false;
 }
 
-static void cmd_set_scan_filter_transport(const char *arg)
+static void cmd_scan_filter_transport(int argc, char *argv[])
 {
-       g_free(filtered_scan_transport);
+       if (argc < 2 || !strlen(argv[1])) {
+               if (filter.transport)
+                       bt_shell_printf("Transport: %s\n",
+                                       filter.transport);
+               return;
+       }
 
-       if (!arg || !strlen(arg))
-               filtered_scan_transport = NULL;
-       else
-               filtered_scan_transport = g_strdup(arg);
+       g_free(filter.transport);
+       filter.transport = g_strdup(argv[1]);
 
-       cmd_set_scan_filter_commit();
+       filter.set = false;
 }
 
-static void clear_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
+static void cmd_scan_filter_duplicate_data(int argc, char *argv[])
 {
-       DBusMessageIter dict;
+       if (argc < 2 || !strlen(argv[1])) {
+               bt_shell_printf("DuplicateData: %s\n",
+                               filter.duplicate ? "on" : "off");
+               return;
+       }
 
-       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 (!strcmp(argv[1], "on"))
+               filter.duplicate = true;
+       else if (!strcmp(argv[1], "off"))
+               filter.duplicate = false;
+       else {
+               bt_shell_printf("Invalid option: %s\n", argv[1]);
+               return;
+       }
 
-       dbus_message_iter_close_container(iter, &dict);
+       filter.set = false;
 }
 
-static void cmd_set_scan_filter_clear(const char *arg)
+static void filter_clear_uuids(void)
 {
-       /* set default values for all options */
-       filtered_scan_rssi = DISTANCE_VAL_INVALID;
-       filtered_scan_pathloss = DISTANCE_VAL_INVALID;
-       g_strfreev(filtered_scan_uuids);
-       filtered_scan_uuids = NULL;
-       filtered_scan_uuids_len = 0;
-       g_free(filtered_scan_transport);
-       filtered_scan_transport = NULL;
+       g_strfreev(filter.uuids);
+       filter.uuids = NULL;
+       filter.uuids_len = 0;
+}
 
-       if (check_default_ctrl() == FALSE)
-               return;
+static void filter_clear_rssi(void)
+{
+       filter.rssi = DISTANCE_VAL_INVALID;
+}
 
-       if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
-               clear_discovery_filter_setup, set_discovery_filter_reply,
-               NULL, NULL) == FALSE) {
-               rl_printf("Failed to clear discovery filter\n");
-       }
+static void filter_clear_pathloss(void)
+{
+       filter.pathloss = DISTANCE_VAL_INVALID;
 }
 
-static struct GDBusProxy *find_device(const char *arg)
+static void filter_clear_transport(void)
 {
-       GDBusProxy *proxy;
+       g_free(filter.transport);
+       filter.transport = NULL;
+}
 
-       if (!arg || !strlen(arg)) {
-               if (default_dev)
-                       return default_dev;
-               rl_printf("Missing device address argument\n");
-               return NULL;
+static void filter_clear_duplicate(void)
+{
+       filter.duplicate = false;
+}
+
+static const struct filter_clear {
+       const char *name;
+       void (*clear) (void);
+} filter_clear[] = {
+       { "uuids", filter_clear_uuids },
+       { "rssi", filter_clear_rssi },
+       { "pathloss", filter_clear_pathloss },
+       { "transport", filter_clear_transport },
+       { "duplicate-data", filter_clear_duplicate },
+       {}
+};
+
+static char *filter_clear_generator(const char *text, int state)
+{
+       static int index, len;
+       const char *arg;
+
+       if (!state) {
+               index = 0;
+               len = strlen(text);
        }
 
-       if (check_default_ctrl() == FALSE)
-               return NULL;
+       while ((arg = filter_clear[index].name)) {
+               index++;
 
-       proxy = find_proxy_by_address(default_ctrl->devices, arg);
-       if (!proxy) {
-               rl_printf("Device %s not available\n", arg);
-               return NULL;
+               if (!strncmp(arg, text, len))
+                       return strdup(arg);
        }
 
-       return proxy;
+       return NULL;
 }
 
-static void cmd_info(const char *arg)
+static void cmd_scan_filter_clear(int argc, char *argv[])
+{
+       const struct filter_clear *fc;
+       bool all = false;
+
+       if (argc < 2 || !strlen(argv[1]))
+               all = true;
+
+       for (fc = filter_clear; fc && fc->name; fc++) {
+               if (all || !strcmp(fc->name, argv[1])) {
+                       fc->clear();
+                       filter.set = false;
+                       if (!all)
+                               goto done;
+               }
+       }
+
+       if (!all) {
+               bt_shell_printf("Invalid argument %s\n", argv[1]);
+               return;
+       }
+
+done:
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       set_discovery_filter();
+}
+
+static struct GDBusProxy *find_device(int argc, char *argv[])
+{
+       GDBusProxy *proxy;
+
+       if (argc < 2 || !strlen(argv[1])) {
+               if (default_dev)
+                       return default_dev;
+               bt_shell_printf("Missing device address argument\n");
+               return NULL;
+       }
+
+       if (check_default_ctrl() == FALSE)
+               return NULL;
+
+       proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
+       if (!proxy) {
+               bt_shell_printf("Device %s not available\n", argv[1]);
+               return NULL;
+       }
+
+       return proxy;
+}
+
+static void cmd_info(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        DBusMessageIter iter;
        const char *address;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
@@ -1357,7 +1521,16 @@ static void cmd_info(const char *arg)
                return;
 
        dbus_message_iter_get_basic(&iter, &address);
-       rl_printf("Device %s\n", address);
+
+       if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) {
+               const char *type;
+
+               dbus_message_iter_get_basic(&iter, &type);
+
+               bt_shell_printf("Device %s (%s)\n", address, type);
+       } else {
+               bt_shell_printf("Device %s\n", address);
+       }
 
        print_property(proxy, "Name");
        print_property(proxy, "Alias");
@@ -1387,44 +1560,44 @@ static void pair_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to pair: %s\n", error.name);
+               bt_shell_printf("Failed to pair: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Pairing successful\n");
+       bt_shell_printf("Pairing successful\n");
 }
 
-static void cmd_pair(const char *arg)
+static void cmd_pair(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to pair\n");
+               bt_shell_printf("Failed to pair\n");
                return;
        }
 
-       rl_printf("Attempting to pair with %s\n", arg);
+       bt_shell_printf("Attempting to pair with %s\n", argv[1]);
 }
 
-static void cmd_trust(const char *arg)
+static void cmd_trust(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        dbus_bool_t trusted;
        char *str;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        trusted = TRUE;
 
-       str = g_strdup_printf("%s trust", arg);
+       str = g_strdup_printf("%s trust", argv[1]);
 
        if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
                                        DBUS_TYPE_BOOLEAN, &trusted,
@@ -1434,19 +1607,19 @@ static void cmd_trust(const char *arg)
        g_free(str);
 }
 
-static void cmd_untrust(const char *arg)
+static void cmd_untrust(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        dbus_bool_t trusted;
        char *str;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        trusted = FALSE;
 
-       str = g_strdup_printf("%s untrust", arg);
+       str = g_strdup_printf("%s untrust", argv[1]);
 
        if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
                                        DBUS_TYPE_BOOLEAN, &trusted,
@@ -1456,19 +1629,19 @@ static void cmd_untrust(const char *arg)
        g_free(str);
 }
 
-static void cmd_block(const char *arg)
+static void cmd_block(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        dbus_bool_t blocked;
        char *str;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        blocked = TRUE;
 
-       str = g_strdup_printf("%s block", arg);
+       str = g_strdup_printf("%s block", argv[1]);
 
        if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
                                        DBUS_TYPE_BOOLEAN, &blocked,
@@ -1478,19 +1651,19 @@ static void cmd_block(const char *arg)
        g_free(str);
 }
 
-static void cmd_unblock(const char *arg)
+static void cmd_unblock(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        dbus_bool_t blocked;
        char *str;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        blocked = FALSE;
 
-       str = g_strdup_printf("%s unblock", arg);
+       str = g_strdup_printf("%s unblock", argv[1]);
 
        if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
                                        DBUS_TYPE_BOOLEAN, &blocked,
@@ -1507,12 +1680,12 @@ static void remove_device_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to remove device: %s\n", error.name);
+               bt_shell_printf("Failed to remove device: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Device has been removed\n");
+       bt_shell_printf("Device has been removed\n");
 }
 
 static void remove_device_setup(DBusMessageIter *iter, void *user_data)
@@ -1535,24 +1708,19 @@ static void remove_device(GDBusProxy *proxy)
                                                remove_device_setup,
                                                remove_device_reply,
                                                path, g_free) == FALSE) {
-               rl_printf("Failed to remove device\n");
+               bt_shell_printf("Failed to remove device\n");
                g_free(path);
        }
 }
 
-static void cmd_remove(const char *arg)
+static void cmd_remove(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing device address argument\n");
-               return;
-       }
-
        if (check_default_ctrl() == FALSE)
                return;
 
-       if (strcmp(arg, "*") == 0) {
+       if (strcmp(argv[1], "*") == 0) {
                GList *list;
 
                for (list = default_ctrl->devices; list;
@@ -1564,9 +1732,9 @@ static void cmd_remove(const char *arg)
                return;
        }
 
-       proxy = find_proxy_by_address(default_ctrl->devices, arg);
+       proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
        if (!proxy) {
-               rl_printf("Device %s not available\n", arg);
+               bt_shell_printf("Device %s not available\n", argv[1]);
                return;
        }
 
@@ -1581,41 +1749,36 @@ static void connect_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to connect: %s\n", error.name);
+               bt_shell_printf("Failed to connect: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Connection successful\n");
+       bt_shell_printf("Connection successful\n");
 
        set_default_device(proxy, NULL);
 }
 
-static void cmd_connect(const char *arg)
+static void cmd_connect(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing device address argument\n");
-               return;
-       }
-
        if (check_default_ctrl() == FALSE)
                return;
 
-       proxy = find_proxy_by_address(default_ctrl->devices, arg);
+       proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
        if (!proxy) {
-               rl_printf("Device %s not available\n", arg);
+               bt_shell_printf("Device %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
                                                        proxy, NULL) == FALSE) {
-               rl_printf("Failed to connect\n");
+               bt_shell_printf("Failed to connect\n");
                return;
        }
 
-       rl_printf("Attempting to connect to %s\n", arg);
+       bt_shell_printf("Attempting to connect to %s\n", argv[1]);
 }
 
 static void disconn_reply(DBusMessage *message, void *user_data)
@@ -1626,12 +1789,12 @@ static void disconn_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to disconnect: %s\n", error.name);
+               bt_shell_printf("Failed to disconnect: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Successful disconnected\n");
+       bt_shell_printf("Successful disconnected\n");
 
        if (proxy != default_dev)
                return;
@@ -1639,54 +1802,53 @@ static void disconn_reply(DBusMessage *message, void *user_data)
        set_default_device(NULL, NULL);
 }
 
-static void cmd_disconn(const char *arg)
+static void cmd_disconn(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
                                                        proxy, NULL) == FALSE) {
-               rl_printf("Failed to disconnect\n");
+               bt_shell_printf("Failed to disconnect\n");
                return;
        }
-       if (strlen(arg) == 0) {
+
+       if (argc < 2 || strlen(argv[1]) == 0) {
                DBusMessageIter iter;
+               const char *addr;
 
                if (g_dbus_proxy_get_property(proxy, "Address", &iter) == TRUE)
-                       dbus_message_iter_get_basic(&iter, &arg);
-       }
-       rl_printf("Attempting to disconnect from %s\n", arg);
+                       dbus_message_iter_get_basic(&iter, &addr);
+
+               bt_shell_printf("Attempting to disconnect from %s\n", addr);
+       } else
+               bt_shell_printf("Attempting to disconnect from %s\n", argv[1]);
 }
 
-static void cmd_list_attributes(const char *arg)
+static void cmd_list_attributes(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       proxy = find_device(arg);
+       proxy = find_device(argc, argv);
        if (!proxy)
                return;
 
        gatt_list_attributes(g_dbus_proxy_get_path(proxy));
 }
 
-static void cmd_set_alias(const char *arg)
+static void cmd_set_alias(int argc, char *argv[])
 {
        char *name;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing name argument\n");
-               return;
-       }
-
        if (!default_dev) {
-               rl_printf("No device connected\n");
+               bt_shell_printf("No device connected\n");
                return;
        }
 
-       name = g_strdup(arg);
+       name = g_strdup(argv[1]);
 
        if (g_dbus_proxy_set_property_basic(default_dev, "Alias",
                                        DBUS_TYPE_STRING, &name,
@@ -1696,52 +1858,47 @@ static void cmd_set_alias(const char *arg)
        g_free(name);
 }
 
-static void cmd_select_attribute(const char *arg)
+static void cmd_select_attribute(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing attribute argument\n");
-               return;
-       }
-
        if (!default_dev) {
-               rl_printf("No device connected\n");
+               bt_shell_printf("No device connected\n");
                return;
        }
 
-       proxy = gatt_select_attribute(arg);
+       proxy = gatt_select_attribute(default_attr, argv[1]);
        if (proxy)
                set_default_attribute(proxy);
 }
 
-static struct GDBusProxy *find_attribute(const char *arg)
+static struct GDBusProxy *find_attribute(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (!arg || !strlen(arg)) {
+       if (argc < 2 || !strlen(argv[1])) {
                if (default_attr)
                        return default_attr;
-               rl_printf("Missing attribute argument\n");
+               bt_shell_printf("Missing attribute argument\n");
                return NULL;
        }
 
-       proxy = gatt_select_attribute(arg);
+       proxy = gatt_select_attribute(default_attr, argv[1]);
        if (!proxy) {
-               rl_printf("Attribute %s not available\n", arg);
+               bt_shell_printf("Attribute %s not available\n", argv[1]);
                return NULL;
        }
 
        return proxy;
 }
 
-static void cmd_attribute_info(const char *arg)
+static void cmd_attribute_info(int argc, char *argv[])
 {
        GDBusProxy *proxy;
        DBusMessageIter iter;
        const char *iface, *uuid, *text;
 
-       proxy = find_attribute(arg);
+       proxy = find_attribute(argc, argv);
        if (!proxy)
                return;
 
@@ -1750,20 +1907,20 @@ static void cmd_attribute_info(const char *arg)
 
        dbus_message_iter_get_basic(&iter, &uuid);
 
-       text = uuidstr_to_str(uuid);
+       text = bt_uuidstr_to_str(uuid);
        if (!text)
                text = g_dbus_proxy_get_path(proxy);
 
        iface = g_dbus_proxy_get_interface(proxy);
        if (!strcmp(iface, "org.bluez.GattService1")) {
-               rl_printf("Service - %s\n", text);
+               bt_shell_printf("Service - %s\n", text);
 
                print_property(proxy, "UUID");
                print_property(proxy, "Primary");
                print_property(proxy, "Characteristics");
                print_property(proxy, "Includes");
        } else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
-               rl_printf("Characteristic - %s\n", text);
+               bt_shell_printf("Characteristic - %s\n", text);
 
                print_property(proxy, "UUID");
                print_property(proxy, "Service");
@@ -1772,7 +1929,7 @@ static void cmd_attribute_info(const char *arg)
                print_property(proxy, "Flags");
                print_property(proxy, "Descriptors");
        } else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
-               rl_printf("Descriptor - %s\n", text);
+               bt_shell_printf("Descriptor - %s\n", text);
 
                print_property(proxy, "UUID");
                print_property(proxy, "Characteristic");
@@ -1780,84 +1937,143 @@ static void cmd_attribute_info(const char *arg)
        }
 }
 
-static void cmd_read(const char *arg)
+static void cmd_read(int argc, char *argv[])
 {
        if (!default_attr) {
-               rl_printf("No attribute selected\n");
+               bt_shell_printf("No attribute selected\n");
                return;
        }
 
        gatt_read_attribute(default_attr);
 }
 
-static void cmd_write(const char *arg)
+static void cmd_write(int argc, char *argv[])
 {
-       if (!arg || !strlen(arg)) {
-               rl_printf("Missing data argument\n");
+       if (!default_attr) {
+               bt_shell_printf("No attribute selected\n");
                return;
        }
 
+       gatt_write_attribute(default_attr, argv[1]);
+}
+
+static void cmd_acquire_write(int argc, char *argv[])
+{
+       if (!default_attr) {
+               bt_shell_printf("No attribute selected\n");
+               return;
+       }
+
+       gatt_acquire_write(default_attr, argv[1]);
+}
+
+static void cmd_release_write(int argc, char *argv[])
+{
+       if (!default_attr) {
+               bt_shell_printf("No attribute selected\n");
+               return;
+       }
+
+       gatt_release_write(default_attr, argv[1]);
+}
+
+static void cmd_acquire_notify(int argc, char *argv[])
+{
+       if (!default_attr) {
+               bt_shell_printf("No attribute selected\n");
+               return;
+       }
+
+       gatt_acquire_notify(default_attr, argv[1]);
+}
+
+static void cmd_release_notify(int argc, char *argv[])
+{
        if (!default_attr) {
-               rl_printf("No attribute selected\n");
+               bt_shell_printf("No attribute selected\n");
                return;
        }
 
-       gatt_write_attribute(default_attr, arg);
+       gatt_release_notify(default_attr, argv[1]);
 }
 
-static void cmd_notify(const char *arg)
+static void cmd_notify(int argc, char *argv[])
 {
        dbus_bool_t enable;
 
-       if (parse_argument_on_off(arg, &enable) == FALSE)
+       if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
                return;
 
        if (!default_attr) {
-               rl_printf("No attribute selected\n");
+               bt_shell_printf("No attribute selected\n");
                return;
        }
 
        gatt_notify_attribute(default_attr, enable ? true : false);
 }
 
-static void cmd_register_profile(const char *arg)
+static void cmd_register_app(int argc, char *argv[])
 {
-       wordexp_t w;
+       if (check_default_ctrl() == FALSE)
+               return;
 
+       gatt_register_app(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_unregister_app(int argc, char *argv[])
+{
        if (check_default_ctrl() == FALSE)
                return;
 
-       if (wordexp(arg, &w, WRDE_NOCMD)) {
-               rl_printf("Invalid argument\n");
+       gatt_unregister_app(dbus_conn, default_ctrl->proxy);
+}
+
+static void cmd_register_service(int argc, char *argv[])
+{
+       if (check_default_ctrl() == FALSE)
                return;
-       }
 
-       if (w.we_wordc == 0) {
-               rl_printf("Missing argument\n");
+       gatt_register_service(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_unregister_service(int argc, char *argv[])
+{
+       if (check_default_ctrl() == FALSE)
                return;
-       }
 
-       gatt_register_profile(dbus_conn, default_ctrl->proxy, &w);
+       gatt_unregister_service(dbus_conn, default_ctrl->proxy, argc, argv);
+}
+
+static void cmd_register_characteristic(int argc, char *argv[])
+{
+       if (check_default_ctrl() == FALSE)
+               return;
 
-       wordfree(&w);
+       gatt_register_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
 }
 
-static void cmd_unregister_profile(const char *arg)
+static void cmd_unregister_characteristic(int argc, char *argv[])
 {
        if (check_default_ctrl() == FALSE)
                return;
 
-       gatt_unregister_profile(dbus_conn, default_ctrl->proxy);
+       gatt_unregister_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
 }
 
-static void cmd_version(const char *arg)
+static void cmd_register_descriptor(int argc, char *argv[])
 {
-       rl_printf("Version %s\n", VERSION);
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       gatt_register_desc(dbus_conn, default_ctrl->proxy, argc, argv);
 }
 
-static void cmd_quit(const char *arg)
+static void cmd_unregister_descriptor(int argc, char *argv[])
 {
-       g_main_loop_quit(main_loop);
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       gatt_unregister_desc(dbus_conn, default_ctrl->proxy, argc, argv);
 }
 
 static char *generic_generator(const char *text, int state,
@@ -1884,9 +2100,9 @@ static char *generic_generator(const char *text, int state,
 
                dbus_message_iter_get_basic(&iter, &str);
 
-               if (!strncmp(str, text, len))
+               if (!strncasecmp(str, text, len))
                        return strdup(str);
-        }
+       }
 
        return NULL;
 }
@@ -1916,7 +2132,7 @@ static char *ctrl_generator(const char *text, int state)
 
                dbus_message_iter_get_basic(&iter, &str);
 
-               if (!strncmp(str, text, len))
+               if (!strncasecmp(str, text, len))
                        return strdup(str);
        }
 
@@ -1934,7 +2150,8 @@ static char *attribute_generator(const char *text, int state)
        return gatt_attribute_generator(text, state);
 }
 
-static char *capability_generator(const char *text, int state)
+static char *argument_generator(const char *text, int state,
+                                       const char * const *args_list)
 {
        static int index, len;
        const char *arg;
@@ -1944,7 +2161,7 @@ static char *capability_generator(const char *text, int state)
                len = strlen(text);
        }
 
-       while ((arg = agent_arguments[index])) {
+       while ((arg = args_list[index])) {
                index++;
 
                if (!strncmp(arg, text, len))
@@ -1954,121 +2171,229 @@ static char *capability_generator(const char *text, int state)
        return NULL;
 }
 
-static gboolean parse_argument_advertise(const char *arg, dbus_bool_t *value,
-                                                       const char **type)
+static char *mode_generator(const char *text, int state)
 {
-       const char * const *opt;
-
-       if (arg == NULL || strlen(arg) == 0) {
-               rl_printf("Missing on/off/type argument\n");
-               return FALSE;
-       }
-
-       if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
-               *value = TRUE;
-               *type = "";
-               return TRUE;
-       }
-
-       if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
-               *value = FALSE;
-               return TRUE;
-       }
-
-       for (opt = ad_arguments; *opt; opt++) {
-               if (strcmp(arg, *opt) == 0) {
-                       *value = TRUE;
-                       *type = *opt;
-                       return TRUE;
-               }
-       }
+       return argument_generator(text, state, mode_arguments);
+}
 
-       rl_printf("Invalid argument %s\n", arg);
-       return FALSE;
+static char *capability_generator(const char *text, int state)
+{
+       return argument_generator(text, state, agent_arguments);
 }
 
-static void cmd_advertise(const char *arg)
+static void cmd_advertise(int argc, char *argv[])
 {
        dbus_bool_t enable;
        const char *type;
 
-       if (parse_argument_advertise(arg, &enable, &type) == FALSE)
+       if (!parse_argument(argc, argv, ad_arguments, "type",
+                                       &enable, &type))
                return;
 
-       if (!ad_manager) {
-               rl_printf("LEAdvertisingManager not found\n");
+       if (!default_ctrl || !default_ctrl->ad_proxy) {
+               bt_shell_printf("LEAdvertisingManager not found\n");
                return;
        }
 
        if (enable == TRUE)
-               ad_register(dbus_conn, ad_manager, type);
+               ad_register(dbus_conn, default_ctrl->ad_proxy, type);
        else
-               ad_unregister(dbus_conn, ad_manager);
+               ad_unregister(dbus_conn, default_ctrl->ad_proxy);
 }
 
 static char *ad_generator(const char *text, int state)
 {
-       static int index, len;
-       const char *arg;
-
-       if (!state) {
-               index = 0;
-               len = strlen(text);
-       }
-
-       while ((arg = ad_arguments[index])) {
-               index++;
+       return argument_generator(text, state, ad_arguments);
+}
 
-               if (!strncmp(arg, text, len))
-                       return strdup(arg);
-       }
+static void cmd_set_advertise_uuids(int argc, char *argv[])
+{
+       ad_advertise_uuids(dbus_conn, argc, argv);
+}
 
-       return NULL;
+static void cmd_set_advertise_service(int argc, char *argv[])
+{
+       ad_advertise_service(dbus_conn, argc, argv);
 }
 
-static void cmd_set_advertise_uuids(const char *arg)
+static void cmd_set_advertise_manufacturer(int argc, char *argv[])
 {
-       ad_advertise_uuids(arg);
+       ad_advertise_manufacturer(dbus_conn, argc, argv);
 }
 
-static void cmd_set_advertise_service(const char *arg)
+static void cmd_set_advertise_tx_power(int argc, char *argv[])
 {
-       ad_advertise_service(arg);
+       dbus_bool_t powered;
+
+       if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
+               return;
+
+       ad_advertise_tx_power(dbus_conn, powered);
 }
 
-static void cmd_set_advertise_manufacturer(const char *arg)
+static void cmd_set_advertise_name(int argc, char *argv[])
 {
-       ad_advertise_manufacturer(arg);
+       if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
+               ad_advertise_name(dbus_conn, true);
+               return;
+       }
+
+       if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
+               ad_advertise_name(dbus_conn, false);
+               return;
+       }
+
+       ad_advertise_local_name(dbus_conn, argv[1]);
 }
 
-static void cmd_set_advertise_tx_power(const char *arg)
+static void cmd_set_advertise_appearance(int argc, char *argv[])
 {
-       if (arg == NULL || strlen(arg) == 0) {
-               rl_printf("Missing on/off argument\n");
+       long int value;
+       char *endptr = NULL;
+
+       if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
+               ad_advertise_appearance(dbus_conn, true);
                return;
        }
 
-       if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) {
-               ad_advertise_tx_power(TRUE);
+       if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
+               ad_advertise_appearance(dbus_conn, false);
                return;
        }
 
-       if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) {
-               ad_advertise_tx_power(FALSE);
+       value = strtol(argv[1], &endptr, 0);
+       if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+               bt_shell_printf("Invalid argument\n");
                return;
        }
 
-       rl_printf("Invalid argument\n");
+       ad_advertise_local_appearance(dbus_conn, value);
 }
 
-static const struct {
-       const char *cmd;
-       const char *arg;
-       void (*func) (const char *arg);
-       const char *desc;
-       char * (*gen) (const char *text, int state);
-       void (*disp) (char **matches, int num_matches, int max_length);
-} cmd_table[] = {
+static void cmd_set_advertise_duration(int argc, char *argv[])
+{
+       long int value;
+       char *endptr = NULL;
+
+       value = strtol(argv[1], &endptr, 0);
+       if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+               bt_shell_printf("Invalid argument\n");
+               return;
+       }
+
+       ad_advertise_duration(dbus_conn, value);
+}
+
+static void cmd_set_advertise_timeout(int argc, char *argv[])
+{
+       long int value;
+       char *endptr = NULL;
+
+       value = strtol(argv[1], &endptr, 0);
+       if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
+               bt_shell_printf("Invalid argument\n");
+               return;
+       }
+
+       ad_advertise_timeout(dbus_conn, value);
+}
+
+static const struct bt_shell_menu advertise_menu = {
+       .name = "advertise",
+       .desc = "Advertise Options Submenu",
+       .entries = {
+       { "set-uuids", "[uuid1 uuid2 ...]",
+                       cmd_set_advertise_uuids, "Set advertise uuids" },
+       { "set-service", "[uuid] [data=xx xx ...]", cmd_set_advertise_service,
+                       "Set advertise service data" },
+       { "set-manufacturer", "[id] [data=xx xx ...]",
+                       cmd_set_advertise_manufacturer,
+                       "Set advertise manufacturer data" },
+       { "set-tx-power", "<on/off>", cmd_set_advertise_tx_power,
+                       "Enable/disable TX power to be advertised",
+                                                       mode_generator },
+       { "set-name", "<on/off/name>", cmd_set_advertise_name,
+                       "Enable/disable local name to be advertised" },
+       { "set-appearance", "<value>", cmd_set_advertise_appearance,
+                       "Set custom appearance to be advertised" },
+       { "set-duration", "<seconds>", cmd_set_advertise_duration,
+                       "Set advertise duration" },
+       { "set-timeout", "<seconds>", cmd_set_advertise_timeout,
+                       "Set advertise timeout" },
+       { } },
+};
+
+static const struct bt_shell_menu scan_menu = {
+       .name = "scan",
+       .desc = "Scan Options Submenu",
+       .entries = {
+       { "uuids", "[all/uuid1 uuid2 ...]", cmd_scan_filter_uuids,
+                               "Set/Get UUIDs filter" },
+       { "rssi", "[rssi]", cmd_scan_filter_rssi,
+                               "Set/Get RSSI filter, and clears pathloss" },
+       { "pathloss", "[pathloss]", cmd_scan_filter_pathloss,
+                               "Set/Get Pathloss filter, and clears RSSI" },
+       { "transport", "[transport]", cmd_scan_filter_transport,
+                               "Set/Get transport filter" },
+       { "duplicate-data", "[on/off]", cmd_scan_filter_duplicate_data,
+                               "Set/Get duplicate data filter",
+                               mode_generator },
+       { "clear", "[uuids/rssi/pathloss/transport/duplicate-data]",
+                               cmd_scan_filter_clear,
+                               "Clears discovery filter.",
+                               filter_clear_generator },
+       { } },
+};
+
+static const struct bt_shell_menu gatt_menu = {
+       .name = "gatt",
+       .desc = "Generic Attribute Submenu",
+       .entries = {
+       { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
+                                                       dev_generator },
+       { "select-attribute", "<attribute/UUID>",  cmd_select_attribute,
+                               "Select attribute", attribute_generator },
+       { "attribute-info", "[attribute/UUID]",  cmd_attribute_info,
+                               "Select attribute", attribute_generator },
+       { "read",         NULL,       cmd_read, "Read attribute value" },
+       { "write",        "<data=xx xx ...>", cmd_write,
+                                               "Write attribute value" },
+       { "acquire-write", NULL, cmd_acquire_write,
+                                       "Acquire Write file descriptor" },
+       { "release-write", NULL, cmd_release_write,
+                                       "Release Write file descriptor" },
+       { "acquire-notify", NULL, cmd_acquire_notify,
+                                       "Acquire Notify file descriptor" },
+       { "release-notify", NULL, cmd_release_notify,
+                                       "Release Notify file descriptor" },
+       { "notify",       "<on/off>", cmd_notify, "Notify attribute value",
+                                                       mode_generator },
+       { "register-application", "[UUID ...]", cmd_register_app,
+                                               "Register profile to connect" },
+       { "unregister-application", NULL, cmd_unregister_app,
+                                               "Unregister profile" },
+       { "register-service", "<UUID>", cmd_register_service,
+                                       "Register application service."  },
+       { "unregister-service", "<UUID/object>", cmd_unregister_service,
+                                       "Unregister application service" },
+       { "register-characteristic", "<UUID> <Flags=read,write,notify...>",
+                                       cmd_register_characteristic,
+                                       "Register application characteristic" },
+       { "unregister-characteristic", "<UUID/object>",
+                               cmd_unregister_characteristic,
+                               "Unregister application characteristic" },
+       { "register-descriptor", "<UUID> <Flags=read,write...>",
+                                       cmd_register_descriptor,
+                                       "Register application descriptor" },
+       { "unregister-descriptor", "<UUID/object>",
+                                       cmd_unregister_descriptor,
+                                       "Unregister application descriptor" },
+       { } },
+};
+
+static const struct bt_shell_menu main_menu = {
+       .name = "main",
+       .entries = {
        { "list",         NULL,       cmd_list, "List available controllers" },
        { "show",         "[ctrl]",   cmd_show, "Controller information",
                                                        ctrl_generator },
@@ -2077,13 +2402,18 @@ static const struct {
        { "devices",      NULL,       cmd_devices, "List available devices" },
        { "paired-devices", NULL,     cmd_paired_devices,
                                        "List paired devices"},
-       { "system-alias", "<name>",   cmd_system_alias },
-       { "reset-alias",  NULL,       cmd_reset_alias },
-       { "power",        "<on/off>", cmd_power, "Set controller power" },
+       { "system-alias", "<name>",   cmd_system_alias,
+                                       "Set controller alias" },
+       { "reset-alias",  NULL,       cmd_reset_alias,
+                                       "Reset controller alias" },
+       { "power",        "<on/off>", cmd_power, "Set controller power",
+                                                       mode_generator },
        { "pairable",     "<on/off>", cmd_pairable,
-                                       "Set controller pairable mode" },
+                                       "Set controller pairable mode",
+                                                       mode_generator },
        { "discoverable", "<on/off>", cmd_discoverable,
-                                       "Set controller discoverable mode" },
+                                       "Set controller discoverable mode",
+                                                       mode_generator },
        { "agent",        "<on/off/capability>", cmd_agent,
                                "Enable/disable agent with given capability",
                                                        capability_generator},
@@ -2092,29 +2422,9 @@ static const struct {
        { "advertise",    "<on/off/type>", cmd_advertise,
                                "Enable/disable advertising with given type",
                                                        ad_generator},
-       { "set-advertise-uuids", "[uuid1 uuid2 ...]",
-                       cmd_set_advertise_uuids, "Set advertise uuids" },
-       { "set-advertise-service", "[uuid][data=[xx xx ...]",
-                       cmd_set_advertise_service,
-                       "Set advertise service data" },
-       { "set-advertise-manufacturer", "[id][data=[xx xx ...]",
-                       cmd_set_advertise_manufacturer,
-                       "Set advertise manufacturer data" },
-       { "set-advertise-tx-power", "<on/off>",
-                       cmd_set_advertise_tx_power,
-                       "Enable/disable TX power to be advertised" },
-       { "set-scan-filter-uuids", "[uuid1 uuid2 ...]",
-                       cmd_set_scan_filter_uuids, "Set scan filter uuids" },
-       { "set-scan-filter-rssi", "[rssi]", cmd_set_scan_filter_rssi,
-                               "Set scan filter rssi, and clears pathloss" },
-       { "set-scan-filter-pathloss", "[pathloss]",
-                                               cmd_set_scan_filter_pathloss,
-                               "Set scan filter pathloss, and clears rssi" },
-       { "set-scan-filter-transport", "[transport]",
-               cmd_set_scan_filter_transport, "Set scan filter transport" },
-       { "set-scan-filter-clear", "", cmd_set_scan_filter_clear,
-                                               "Clears discovery filter." },
-       { "scan",         "<on/off>", cmd_scan, "Scan for devices" },
+       { "set-alias",    "<alias>",  cmd_set_alias, "Set device alias" },
+       { "scan",         "<on/off>", cmd_scan, "Scan for devices",
+                                                       mode_generator },
        { "info",         "[dev]",    cmd_info, "Device information",
                                                        dev_generator },
        { "pair",         "[dev]",    cmd_pair, "Pair with device",
@@ -2133,296 +2443,56 @@ static const struct {
                                                        dev_generator },
        { "disconnect",   "[dev]",    cmd_disconn, "Disconnect device",
                                                        dev_generator },
-       { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
-                                                       dev_generator },
-       { "set-alias",    "<alias>",  cmd_set_alias, "Set device alias" },
-       { "select-attribute", "<attribute>",  cmd_select_attribute,
-                               "Select attribute", attribute_generator },
-       { "attribute-info", "[attribute]",  cmd_attribute_info,
-                               "Select attribute", attribute_generator },
-       { "read",         NULL,       cmd_read, "Read attribute value" },
-       { "write",        "<data=[xx xx ...]>", cmd_write,
-                                               "Write attribute value" },
-       { "notify",       "<on/off>", cmd_notify, "Notify attribute value" },
-       { "register-profile", "<UUID ...>", cmd_register_profile,
-                                               "Register profile to connect" },
-       { "unregister-profile", NULL, cmd_unregister_profile,
-                                               "Unregister profile" },
-       { "version",      NULL,       cmd_version, "Display version" },
-       { "quit",         NULL,       cmd_quit, "Quit program" },
-       { "exit",         NULL,       cmd_quit },
-       { "help" },
-       { }
+       { } },
 };
 
-static char *cmd_generator(const char *text, int state)
-{
-       static int index, len;
-       const char *cmd;
-
-       if (!state) {
-               index = 0;
-               len = strlen(text);
-       }
-
-       while ((cmd = cmd_table[index].cmd)) {
-               index++;
-
-               if (!strncmp(cmd, text, len))
-                       return strdup(cmd);
-       }
-
-       return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
-       char **matches = NULL;
-
-       if (agent_completion() == TRUE) {
-               rl_attempted_completion_over = 1;
-               return NULL;
-       }
-
-       if (start > 0) {
-               int i;
-
-               for (i = 0; cmd_table[i].cmd; i++) {
-                       if (strncmp(cmd_table[i].cmd,
-                                       rl_line_buffer, start - 1))
-                               continue;
-
-                       if (!cmd_table[i].gen)
-                               continue;
-
-                       rl_completion_display_matches_hook = cmd_table[i].disp;
-                       matches = rl_completion_matches(text, cmd_table[i].gen);
-                       break;
-               }
-       } else {
-               rl_completion_display_matches_hook = NULL;
-               matches = rl_completion_matches(text, cmd_generator);
-       }
-
-       if (!matches)
-               rl_attempted_completion_over = 1;
-
-       return matches;
-}
-
-static void rl_handler(char *input)
-{
-       char *cmd, *arg;
-       int i;
-
-       if (!input) {
-               rl_insert_text("quit");
-               rl_redisplay();
-               rl_crlf();
-               g_main_loop_quit(main_loop);
-               return;
-       }
-
-       if (!strlen(input))
-               goto done;
-
-       if (agent_input(dbus_conn, input) == TRUE)
-               goto done;
-
-       add_history(input);
-
-       cmd = strtok_r(input, " ", &arg);
-       if (!cmd)
-               goto done;
-
-       if (arg) {
-               int len = strlen(arg);
-               if (len > 0 && arg[len - 1] == ' ')
-                       arg[len - 1] = '\0';
-       }
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (strcmp(cmd, cmd_table[i].cmd))
-                       continue;
-
-               if (cmd_table[i].func) {
-                       cmd_table[i].func(arg);
-                       goto done;
-               }
-       }
-
-       if (strcmp(cmd, "help")) {
-               printf("Invalid command\n");
-               goto done;
-       }
-
-       printf("Available commands:\n");
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (cmd_table[i].desc)
-                       printf("  %s %-*s %s\n", cmd_table[i].cmd,
-                                       (int)(25 - strlen(cmd_table[i].cmd)),
-                                       cmd_table[i].arg ? : "",
-                                       cmd_table[i].desc ? : "");
-       }
-
-done:
-       free(input);
-}
-
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
-{
-       static bool terminated = false;
-       struct signalfd_siginfo si;
-       ssize_t result;
-       int fd;
-
-       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               g_main_loop_quit(main_loop);
-               return FALSE;
-       }
-
-       fd = g_io_channel_unix_get_fd(channel);
-
-       result = read(fd, &si, sizeof(si));
-       if (result != sizeof(si))
-               return FALSE;
-
-       switch (si.ssi_signo) {
-       case SIGINT:
-               if (input) {
-                       rl_replace_line("", 0);
-                       rl_crlf();
-                       rl_on_new_line();
-                       rl_redisplay();
-                       break;
-               }
-
-               /*
-                * If input was not yet setup up that means signal was received
-                * while daemon was not yet running. Since user is not able
-                * to terminate client by CTRL-D or typing exit treat this as
-                * exit and fall through.
-                */
-       case SIGTERM:
-               if (!terminated) {
-                       rl_replace_line("", 0);
-                       rl_crlf();
-                       g_main_loop_quit(main_loop);
-               }
-
-               terminated = true;
-               break;
-       }
-
-       return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
-       GIOChannel *channel;
-       guint source;
-       sigset_t mask;
-       int fd;
-
-       sigemptyset(&mask);
-       sigaddset(&mask, SIGINT);
-       sigaddset(&mask, SIGTERM);
-
-       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
-               perror("Failed to set signal mask");
-               return 0;
-       }
-
-       fd = signalfd(-1, &mask, 0);
-       if (fd < 0) {
-               perror("Failed to create signal descriptor");
-               return 0;
-       }
-
-       channel = g_io_channel_unix_new(fd);
-
-       g_io_channel_set_close_on_unref(channel, TRUE);
-       g_io_channel_set_encoding(channel, NULL, NULL);
-       g_io_channel_set_buffered(channel, FALSE);
-
-       source = g_io_add_watch(channel,
-                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               signal_handler, NULL);
-
-       g_io_channel_unref(channel);
-
-       return source;
-}
+static const struct option options[] = {
+       { "agent",      required_argument, 0, 'a' },
+       { 0, 0, 0, 0 }
+};
 
-static gboolean option_version = FALSE;
+static const char *agent_option;
 
-static gboolean parse_agent(const char *key, const char *value,
-                                       gpointer user_data, GError **error)
-{
-       if (value)
-               auto_register_agent = g_strdup(value);
-       else
-               auto_register_agent = g_strdup("");
+static const char **optargs[] = {
+       &agent_option
+};
 
-       return TRUE;
-}
+static const char *help[] = {
+       "Register agent handler: <capability>"
+};
 
-static GOptionEntry options[] = {
-       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
-                               "Show version information and exit" },
-       { "agent", 'a', G_OPTION_FLAG_OPTIONAL_ARG,
-                               G_OPTION_ARG_CALLBACK, parse_agent,
-                               "Register agent handler", "CAPABILITY" },
-       { NULL },
+static const struct bt_shell_opt opt = {
+       .options = options,
+       .optno = sizeof(options) / sizeof(struct option),
+       .optstr = "a:",
+       .optarg = optargs,
+       .help = help,
 };
 
 static void client_ready(GDBusClient *client, void *user_data)
 {
-       if (!input)
-               input = setup_standard_input();
+       setup_standard_input();
 }
 
 int main(int argc, char *argv[])
 {
-       GOptionContext *context;
-       GError *error = NULL;
        GDBusClient *client;
-       guint signal;
 
-       context = g_option_context_new(NULL);
-       g_option_context_add_main_entries(context, options, NULL);
+       bt_shell_init(argc, argv, &opt);
+       bt_shell_set_menu(&main_menu);
+       bt_shell_add_submenu(&advertise_menu);
+       bt_shell_add_submenu(&scan_menu);
+       bt_shell_add_submenu(&gatt_menu);
+       bt_shell_set_prompt(PROMPT_OFF);
 
-       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
-               if (error != NULL) {
-                       g_printerr("%s\n", error->message);
-                       g_error_free(error);
-               } else
-                       g_printerr("An unknown error occurred\n");
-               exit(1);
-       }
-
-       g_option_context_free(context);
-
-       if (option_version == TRUE) {
-               printf("%s\n", VERSION);
-               exit(0);
-       }
+       if (agent_option)
+               auto_register_agent = g_strdup(agent_option);
+       else
+               auto_register_agent = g_strdup("");
 
-       main_loop = g_main_loop_new(NULL, FALSE);
        dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+       g_dbus_attach_object_manager(dbus_conn);
 
-       setlinebuf(stdout);
-       rl_attempted_completion_function = cmd_completion;
-
-       rl_erase_empty_line = 1;
-       rl_callback_handler_install(NULL, rl_handler);
-
-       rl_set_prompt(PROMPT_OFF);
-       rl_redisplay();
-
-       signal = setup_signalfd();
        client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
 
        g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -2434,18 +2504,11 @@ int main(int argc, char *argv[])
 
        g_dbus_client_set_ready_watch(client, client_ready, NULL);
 
-       g_main_loop_run(main_loop);
+       bt_shell_run();
 
        g_dbus_client_unref(client);
-       g_source_remove(signal);
-       if (input > 0)
-               g_source_remove(input);
-
-       rl_message("");
-       rl_callback_handler_remove();
 
        dbus_connection_unref(dbus_conn);
-       g_main_loop_unref(main_loop);
 
        g_list_free_full(ctrl_list, proxy_leak);
 
index ff764f1..2256123 100755 (executable)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.43)
+AC_INIT(bluez, 5.48)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
                                        tar-pax no-dist-gzip dist-xz])
@@ -34,8 +34,13 @@ AC_PROG_LIBTOOL
 
 if (test "$USE_MAINTAINER_MODE" = "yes"); then
        AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
+       AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
+       AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
+       AC_CHECK_HEADERS(valgrind/memcheck.h)
 fi
 AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
+AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
+AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
 
 MISC_FLAGS
 
@@ -187,31 +192,53 @@ AC_ARG_ENABLE(cups, AC_HELP_STRING([--disable-cups],
                 [disable CUPS printer support]), [enable_cups=${enableval}])
 AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no")
 
+AC_ARG_ENABLE(mesh, AC_HELP_STRING([--enable-mesh],
+               [enable Mesh profile support]), [enable_mesh=${enableval}])
+AM_CONDITIONAL(MESH, test "${enable_mesh}" = "yes")
+
+if (test "${enable_mesh}" == "yes"); then
+       PKG_CHECK_MODULES(JSONC, json-c, dummy=yes,
+                               AC_MSG_ERROR(json-c is required))
+       AC_SUBST(JSON_CFLAGS)
+       AC_SUBST(JSON_LIBS)
+fi
 
 AC_ARG_ENABLE(midi, AC_HELP_STRING([--enable-midi],
                 [enable MIDI support]), [enable_midi=${enableval}])
-AM_CONDITIONAL(MIDI, test "${enable_midi}" = "no")
+AM_CONDITIONAL(MIDI, test "${enable_midi}" = "yes")
 
 if (test "${enable_midi}" = "yes"); then
-       PKG_CHECK_MODULES(ALSA, alsa, dummy=no,
+       PKG_CHECK_MODULES(ALSA, alsa, dummy=yes,
                                AC_MSG_ERROR(ALSA lib is required for MIDI support))
        AC_SUBST(ALSA_CFLAGS)
        AC_SUBST(ALSA_LIBS)
 fi
 
+
 AC_ARG_ENABLE(obex, AC_HELP_STRING([--disable-obex],
-               [disable OBEX profile support]), [enable_obex=${enableval}])
-                               
+                [disable OBEX profile support]), [enable_obex=${enableval}])
+
 AC_SUBST(ICAL_CFLAGS)
 AC_SUBST(ICAL_LIBS)
 
 AM_CONDITIONAL(OBEX, test "${enable_obex}" != "no")
 
+AC_ARG_ENABLE(btpclient, AC_HELP_STRING([--enable-btpclient],
+               [enable BTP client]), [enable_btpclient=${enableval}])
+AM_CONDITIONAL(BTPCLIENT, test "${enable_btpclient}" = "yes")
+
+if (test "${enable_btpclient}" = "yes"); then
+       PKG_CHECK_MODULES(ELL, ell >= 0.2, dummy=yes,
+                         AC_MSG_ERROR(ell library >= 0.2 is required))
+       AC_SUBST(ELL_CFLAGS)
+       AC_SUBST(ELL_LIBS)
+fi
+
 AC_ARG_ENABLE(client, AC_HELP_STRING([--disable-client],
                [disable command line client]), [enable_client=${enableval}])
 AM_CONDITIONAL(CLIENT, test "${enable_client}" != "no")
 
-if (test "${enable_client}" != "no"); then
+if (test "${enable_client}" != "no" || test "${enable_mesh}" == "yes"); then
         AC_CHECK_HEADERS(readline/readline.h, enable_readline=yes,
                 AC_MSG_ERROR(readline header files are required))
 fi
@@ -259,6 +286,11 @@ AC_ARG_ENABLE(manpages, AC_HELP_STRING([--enable-manpages],
                                        [enable_manpages=${enableval}])
 AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
 
+AC_ARG_ENABLE(testing, AC_HELP_STRING([--enable-testing],
+                       [enable testing tools]),
+                                       [enable_testing=${enableval}])
+AM_CONDITIONAL(TESTING, test "${enable_testing}" = "yes")
+
 AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
                        [enable experimental tools]),
                                        [enable_experimental=${enableval}])
@@ -320,7 +352,7 @@ AM_CONDITIONAL(TIZEN_LE_PLUGIN, test "${enable_le}" = "yes")
 # End of TIZEN_FEATURE_BLUEZ_MODIFY
 
 AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated],
-                       [enable deprecated plugins (BLE services, ...)]),
+                       [enable deprecated tools]),
                                        [enable_deprecated=${enableval}])
 AM_CONDITIONAL(DEPRECATED, test "${enable_deprecated}" = "yes")
 
index e37781e..d42fce0 100755 (executable)
@@ -54,29 +54,64 @@ Methods             void StartDiscovery()
                        Parameters that may be set in the filter dictionary
                        include the following:
 
-                       array{string} UUIDs     : filtered service UUIDs
-                       int16         RSSI      : RSSI threshold value
-                       uint16        Pathloss  : Pathloss threshold value
-                       string        Transport : type of scan to run
-
-                       When a remote device is found that advertises any UUID
-                       from UUIDs, it will be reported if:
-                       - Pathloss and RSSI are both empty,
-                       - only Pathloss param is set, device advertise TX pwer,
-                         and computed pathloss is less than Pathloss param,
-                       - only RSSI param is set, and received RSSI is higher
-                         than RSSI param,
-
-                       Transport parameter determines the type of scan:
-                               "auto"  - interleaved scan, default value
-                               "bredr" - BR/EDR inquiry
-                               "le"    - LE scan only
-
-                       If "le" or "bredr" Transport is requested, and the
-                       controller doesn't support it, org.bluez.Error.Failed
-                       error will be returned. If "auto" transport is
-                       requested, scan will use LE, BREDR, or both, depending
-                       on what's currently enabled on the controller.
+                       array{string} UUIDs
+
+                               Filter by service UUIDs, empty means match
+                               _any_ UUID.
+
+                               When a remote device is found that advertises
+                               any UUID from UUIDs, it will be reported if:
+                               - Pathloss and RSSI are both empty.
+                               - only Pathloss param is set, device advertise
+                                 TX pwer, and computed pathloss is less than
+                                 Pathloss param.
+                               - only RSSI param is set, and received RSSI is
+                                 higher than RSSI param.
+
+                       int16 RSSI
+
+                               RSSI threshold value.
+
+                               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 StartDiscovery by default,
+                               will not be applied.
+
+                       uint16 Pathloss
+
+                               Pathloss threshold value.
+
+                               PropertiesChanged signals will be emitted
+                               for already existing Device objects, with
+                               updated Pathloss value.
+
+                       string Transport (Default "auto")
+
+                               Transport parameter determines the type of
+                               scan.
+
+                               Possible values:
+                                       "auto"  - interleaved scan
+                                       "bredr" - BR/EDR inquiry
+                                       "le"    - LE scan only
+
+                               If "le" or "bredr" Transport is requested,
+                               and the controller doesn't support it,
+                               org.bluez.Error.Failed error will be returned.
+                               If "auto" transport is requested, scan will use
+                               LE, BREDR, or both, depending on what's
+                               currently enabled on the controller.
+
+                       bool DuplicateData (Default: true)
+
+                               Disables duplicate detection of advertisement
+                               data.
+
+                               When enabled PropertiesChanged signals will be
+                               generated for either ManufacturerData and
+                               ServiceData everytime they are discovered.
 
                        When discovery filter is set, Device objects will be
                        created as new devices with matching criteria are
@@ -84,12 +119,6 @@ Methods             void StartDiscovery()
                        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
-                       StartDiscovery by default, will not be applied.
-
                        When multiple clients call SetDiscoveryFilter, their
                        filters are internally merged, and notifications about
                        new devices are sent to all clients. Therefore, each
@@ -109,6 +138,13 @@ Methods            void StartDiscovery()
                                         org.bluez.Error.NotSupported
                                         org.bluez.Error.Failed
 
+               array{string} GetDiscoveryFilters()
+
+                       Return available filters that can be given to
+                       SetDiscoveryFilter.
+
+                       Possible errors: None
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                void StartCustomDiscovery(string pattern)
 
@@ -266,6 +302,18 @@ Properties string Address [readonly]
 
                        The Bluetooth device address.
 
+               string AddressType [readonly]
+
+                       The Bluetooth  Address Type. For dual-mode and BR/EDR
+                       only adapter this defaults to "public". Single mode LE
+                       adapters may have either value. With privacy enabled
+                       this contains type of Identity Address and not type of
+                       address used for connection.
+
+                       Possible values:
+                               "public" - Public address
+                               "random" - Random address
+
                string Name [readonly]
 
                        The Bluetooth system name (pretty hostname).
index 1c18ae3..15e64d3 100755 (executable)
@@ -61,11 +61,39 @@ Properties  string Type
                        Service Data elements to include. The keys are the
                        UUID to associate with the data.
 
-               bool IncludeTxPower
+               array{string} Includes
 
-                       Includes the Tx Power in the advertising packet.
-                       If missing, the Tx Power is not included.
+                       List of features to be included in the advertising
+                       packet.
 
+                       Possible values: as found on
+                                       LEAdvertisingManager.SupportedIncludes
+
+               string LocalName
+
+                       Local name to be used in the advertising report. If the
+                       string is too big to fit into the packet it will be
+                       truncated.
+
+                       If this property is available 'local-name' cannot be
+                       present in the Includes.
+
+               uint16 Appearance
+
+                       Appearance to be used in the advertising report.
+
+                       Possible values: as found on GAP Service.
+
+               uint16_t Duration
+
+                       Duration of the advertisement in seconds. If there are
+                       other applications advertising no duration is set the
+                       default is 2 seconds.
+
+               uint16_t Timeout
+
+                       Timeout of the advertisement in seconds. This defines
+                       the lifetime of the advertisement.
 
 LE Advertising Manager hierarchy
 ================================
@@ -75,32 +103,56 @@ Data which should be broadcast to devices.  Advertisement Data elements must
 follow the API for LE Advertisement Data described above.
 
 Service                org.bluez
-Interface      org.bluez.LEAdvertisingManager1 [Experimental]
+Interface      org.bluez.LEAdvertisingManager1
 Object path    /org/bluez/{hci0,hci1,...}
 
 Methods                RegisterAdvertisement(object advertisement, dict options)
 
                        Registers an advertisement object to be sent over the LE
                        Advertising channel.  The service must be exported
-                       under interface LEAdvertisement1. InvalidArguments
-                       indicates that the object has invalid or conflicting
-                       properties.  InvalidLength indicates that the data
+                       under interface LEAdvertisement1.
+
+                       InvalidArguments error indicates that the object has
+                       invalid or conflicting properties.
+
+                       InvalidLength error indicates that the data
                        provided generates a data packet which is too long.
-                       The properties of this object are parser when it is
+
+                       The properties of this object are parsed when it is
                        registered, and any changes are ignored.
-                       Currently only one advertisement at a time is supported,
-                       attempting to register two advertisements will result in
+
+                       If the same object is registered twice it will result in
                        an AlreadyExists error.
 
+                       If the maximum number of advertisement instances is
+                       reached it will result in NotPermitted error.
+
                        Possible errors: org.bluez.Error.InvalidArguments
                                         org.bluez.Error.AlreadyExists
                                         org.bluez.Error.InvalidLength
+                                        org.bluez.Error.NotPermitted
 
                UnregisterAdvertisement(object advertisement)
 
                        This unregisters an advertisement that has been
-                       prevously registered.  The object path parameter must
+                       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
+
+Properties     byte ActiveInstances
+
+                       Number of active advertising instances.
+
+               byte SupportedInstances
+
+                       Number of available advertising instances.
+
+               array{string} SupportedIncludes
+
+                       List of supported system includes.
+
+                       Possible values: "tx-power"
+                                        "appearance"
+                                        "local-name"
index 801ccb6..0d9347c 100755 (executable)
@@ -163,7 +163,10 @@ Methods            void Release()
                        This method gets called to request the user to
                        authorize an incoming pairing attempt which
                        would in other circumstances trigger the just-works
-                       model.
+                       model, or when the user plugged in a device that
+                       implements cable pairing. In the latter case, the
+                       device would not be connected to the adapter via
+                       Bluetooth yet.
 
                        Possible errors: org.bluez.Error.Rejected
                                         org.bluez.Error.Canceled
diff --git a/doc/battery-api.txt b/doc/battery-api.txt
new file mode 100644 (file)
index 0000000..dc7dbed
--- /dev/null
@@ -0,0 +1,14 @@
+BlueZ D-Bus Battery API description
+***********************************
+
+
+Battery hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Battery1
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Properties     byte Percentage [readonly]
+
+                       The percentage of battery left as an unsigned 8-bit integer.
index 0bcec4d..2de1535 100644 (file)
@@ -52,6 +52,10 @@ Methods              void Connect()
                        This method can be also used to cancel a preceding
                        Connect call before a reply to it has been received.
 
+                       For non-trusted devices connected over LE bearer calling
+                       this method will disable incoming connections until
+                       Connect method is called again.
+
                        Possible errors: org.bluez.Error.NotConnected
 
                void ConnectProfile(string uuid)
@@ -176,6 +180,19 @@ Properties string Address [readonly]
 
                        The Bluetooth device address of the remote device.
 
+               string AddressType [readonly]
+
+                       The Bluetooth device Address Type. For dual-mode and
+                       BR/EDR only devices this defaults to "public". Single
+                       mode LE devices may have either value. If remote device
+                       uses privacy than before pairing this represents address
+                       type used for connection and Identity Address after
+                       pairing.
+
+                       Possible values:
+                               "public" - Public address
+                               "random" - Random address
+
                string Name [readonly, optional]
 
                        The Bluetooth remote name. This value can not be
index b29f599..5b57b2c 100755 (executable)
@@ -101,12 +101,77 @@ Methods           array{byte} ReadValue(dict options)
                                         org.bluez.Error.NotAuthorized
                                         org.bluez.Error.NotSupported
 
+               fd, uint16 AcquireWrite(dict options) [optional]
+
+                       Acquire file descriptor and MTU for writing. Usage of
+                       WriteValue will be locked causing it to return
+                       NotPermitted error.
+
+                       For server the MTU returned shall be equal or smaller
+                       than the negotiated MTU.
+
+                       For client it only works with characteristic that has
+                       WriteAcquired property which relies on
+                       write-without-response Flag.
+
+                       To release the lock the client shall close the file
+                       descriptor, a HUP is generated in case the device
+                       is disconnected.
+
+                       Note: the MTU can only be negotiated once and is
+                       symmetric therefore this method may be delayed in
+                       order to have the exchange MTU completed, because of
+                       that the file descriptor is closed during
+                       reconnections as the MTU has to be renegotiated.
+
+                       Possible options: "device": Object Device (Server only)
+                                         "MTU": Exchanged MTU (Server only)
+
+                       Possible Errors: org.bluez.Error.Failed
+                                        org.bluez.Error.NotSupported
+
+               fd, uint16 AcquireNotify(dict options) [optional]
+
+                       Acquire file descriptor and MTU for notify. Usage of
+                       StartNotify will be locked causing it to return
+                       NotPermitted error.
+
+                       For server the MTU returned shall be equal or smaller
+                       than the negotiated MTU.
+
+                       Only works with characteristic that has NotifyAcquired
+                       which relies on notify Flag and no other client have
+                       called StartNotify.
+
+                       Notification are enabled during this procedure so
+                       StartNotify shall not be called, any notification
+                       will be dispatched via file descriptor therefore the
+                       Value property is not affected during the time where
+                       notify has been acquired.
+
+                       To release the lock the client shall close the file
+                       descriptor, a HUP is generated in case the device
+                       is disconnected.
+
+                       Note: the MTU can only be negotiated once and is
+                       symmetric therefore this method may be delayed in
+                       order to have the exchange MTU completed, because of
+                       that the file descriptor is closed during
+                       reconnections as the MTU has to be renegotiated.
+
+                       Possible options: "device": Object Device (Server only)
+                                         "MTU": Exchanged MTU (Server only)
+
+                       Possible Errors: org.bluez.Error.Failed
+                                        org.bluez.Error.NotSupported
+
                void StartNotify()
 
                        Starts a notification session from this characteristic
                        if it supports value notifications or indications.
 
                        Possible Errors: org.bluez.Error.Failed
+                                        org.bluez.Error.NotPermitted
                                         org.bluez.Error.InProgress
                                         org.bluez.Error.NotSupported
 
@@ -119,6 +184,13 @@ Methods            array{byte} ReadValue(dict options)
 
                        Possible Errors: org.bluez.Error.Failed
 
+               void Confirm() [optional] (Server only)
+
+                       This method doesn't expect a reply so it is just a
+                       confirmation that value was received.
+
+                       Possible Errors: org.bluez.Error.Failed
+
 Properties     string UUID [read-only]
 
                        128-bit characteristic UUID.
@@ -135,6 +207,28 @@ Properties string UUID [read-only]
                        when a notification or indication is received, upon
                        which a PropertiesChanged signal will be emitted.
 
+               boolean WriteAcquired [read-only, optional]
+
+                       True, if this characteristic has been acquired by any
+                       client using AcquireWrite.
+
+                       For client properties is ommited in case
+                       'write-without-response' flag is not set.
+
+                       For server the presence of this property indicates
+                       that AcquireWrite is supported.
+
+               boolean NotifyAcquired [read-only, optional]
+
+                       True, if this characteristic has been acquired by any
+                       client using AcquireNotify.
+
+                       For client this properties is ommited in case 'notify'
+                       flag is not set.
+
+                       For server the presence of this property indicates
+                       that AcquireNotify is supported.
+
                boolean Notifying [read-only, optional]
 
                        True, if notifications or indications on this
@@ -196,6 +290,7 @@ Methods             array{byte} ReadValue(dict flags)
 
                        Possible options: "offset": Start offset
                                          "device": Device path (Server only)
+                                         "link": Link type (Server only)
 
                        Possible Errors: org.bluez.Error.Failed
                                         org.bluez.Error.InProgress
@@ -210,6 +305,7 @@ Methods             array{byte} ReadValue(dict flags)
 
                        Possible options: "offset": Start offset
                                          "device": Device path (Server only)
+                                         "link": Link type (Server only)
 
                        Possible Errors: org.bluez.Error.Failed
                                         org.bluez.Error.InProgress
index 6690752..32bdfce 100755 (executable)
@@ -28,7 +28,7 @@ Linux kernel v4.2     Version 1.10
 Linux kernel v4.5      Version 1.11
 Linux kernel v4.6      Version 1.12
 Linux kernel v4.8      Version 1.13
-Linux kernel v4.9      Version 1.14 (not yet released)
+Linux kernel v4.9      Version 1.14
 
 Version 1.1 introduces Set Device ID command.
 
diff --git a/doc/oob-api.txt b/doc/oob-api.txt
deleted file mode 100755 (executable)
index d838712..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-BlueZ D-Bus Out Of Band Pairing API description
-===============================================
-
-Copyright (C) 2011  Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
-
-Service                org.bluez
-Interface      org.bluez.OutOfBand
-Object path    [variable prefix]/{hci0,hci1,...}
-
-Methods                array{byte} hash, array{byte} randomizer ReadLocalData()
-
-                       This method reads local OOB data from adapter. Return
-                       value is pair of arrays 16 bytes each.
-
-                       Note: This method will generate and return new local
-                       OOB data.
-
-                       Possible errors: org.bluez.Error.Failed
-                                        org.bluez.Error.InProgress
-
-               void AddRemoteData(string address, array{byte} hash,
-                                                       array{byte} randomizer)
-
-                       This method adds new Out Of Band data for
-                       specified address. If data for specified address
-                       already exists it will be overwritten with new one.
-
-                       Possible errors: org.bluez.Error.Failed
-                                        org.bluez.Error.InvalidArguments
-
-               void RemoveRemoteData(string address)
-
-                       This method removes Out Of Band data for specified
-                       address. If data for specified address does not exist
-                       nothing is removed.
-
-                       Possible errors: org.bluez.Error.Failed
-                                        org.bluez.Error.InvalidArguments
index 72e17ce..fe0a0d4 100755 (executable)
@@ -32,6 +32,27 @@ minimal options for a successful boot.
        CONFIG_DEVTMPFS=y
        CONFIG_DEBUG_FS=y
 
+For Bluetooth functionality:
+
+       CONFIG_BT=y
+       CONFIG_BT_BREDR=y
+       CONFIG_BT_RFCOMM=y
+       CONFIG_BT_BNEP=y
+       CONFIG_BT_HIDP=y
+       CONFIG_BT_LE=y
+
+       CONFIG_BT_HCIVHCI=y
+
+       CONFIG_CRYPTO_CMAC=y
+       CONFIG_CRYPTO_USER_API=y
+       CONFIG_CRYPTO_USER_API_HASH=y
+       CONFIG_CRYPTO_USER_API_SKCIPHER=y
+
+       CONFIG_UNIX=y
+
+       CONFIG_UHID=y
+
+
 These options should be installed as .config in the kernel source directory
 followed by this command.
 
index 1e94fc3..a9b225a 100755 (executable)
@@ -600,7 +600,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
 
        switch (btdev->type) {
        case BTDEV_TYPE_BREDRLE:
-               btdev->version = 0x08;
+               btdev->version = 0x09;
                set_bredrle_features(btdev);
                set_bredrle_commands(btdev);
                break;
@@ -610,7 +610,7 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
                set_bredr_commands(btdev);
                break;
        case BTDEV_TYPE_LE:
-               btdev->version = 0x08;
+               btdev->version = 0x09;
                set_le_features(btdev);
                set_le_commands(btdev);
                break;
index d7ee297..1c8ba28 100755 (executable)
 #define MAX_RX_LEN             0x00fb
 #define MAX_RX_TIME            0x0848
 
+#define DEFAULT_ALL_PHYS       0x03
+#define DEFAULT_TX_PHYS                0x00
+#define DEFAULT_RX_PHYS                0x00
+
 struct bt_peer {
        uint8_t  addr_type;
        uint8_t  addr[6];
@@ -123,6 +127,10 @@ struct bt_le {
        uint8_t  le_resolv_enable;
        uint16_t le_resolv_timeout;
 
+       uint8_t  le_default_all_phys;
+       uint8_t  le_default_tx_phys;
+       uint8_t  le_default_rx_phys;
+
        struct bt_peer scan_cache[SCAN_CACHE_SIZE];
        uint8_t scan_cache_count;
 };
@@ -281,6 +289,37 @@ static void reset_defaults(struct bt_le *hci)
        hci->commands[35] |= 0x02;      /* LE Set Address Resolution Enable */
        hci->commands[35] |= 0x04;      /* LE Set Resolvable Private Address Timeout */
        hci->commands[35] |= 0x08;      /* LE Read Maximum Data Length */
+       hci->commands[35] |= 0x10;      /* LE Read PHY */
+       hci->commands[35] |= 0x20;      /* LE Set Default PHY */
+       hci->commands[35] |= 0x40;      /* LE Set PHY */
+       //hci->commands[35] |= 0x80;    /* LE Enhanced Receiver Test */
+       //hci->commands[36] |= 0x01;    /* LE Enhanced Transmitter Test */
+       //hci->commands[36] |= 0x02;    /* LE Set Advertising Set Random Address */
+       //hci->commands[36] |= 0x04;    /* LE Set Extended Advertising Parameters */
+       //hci->commands[36] |= 0x08;    /* LE Set Extended Advertising Data */
+       //hci->commands[36] |= 0x10;    /* LE Set Extended Scan Response Data */
+       //hci->commands[36] |= 0x20;    /* LE Set Extended Advertising Enable */
+       //hci->commands[36] |= 0x40;    /* LE Read Maximum Advertising Data Length */
+       //hci->commands[36] |= 0x80;    /* LE Read Number of Supported Advertising Sets */
+       //hci->commands[37] |= 0x01;    /* LE Remove Advertising Set */
+       //hci->commands[37] |= 0x02;    /* LE Clear Advertising Sets */
+       //hci->commands[37] |= 0x04;    /* LE Set Periodic Advertising Parameters */
+       //hci->commands[37] |= 0x08;    /* LE Set Periodic Advertising Data */
+       //hci->commands[37] |= 0x10;    /* LE Set Periodic Advertising Enable */
+       //hci->commands[37] |= 0x20;    /* LE Set Extended Scan Parameters */
+       //hci->commands[37] |= 0x40;    /* LE Set Extended Scan Enable */
+       //hci->commands[37] |= 0x80;    /* LE Extended Create Connection */
+       //hci->commands[38] |= 0x01;    /* LE Periodic Advertising Create Sync */
+       //hci->commands[38] |= 0x02;    /* LE Periodic Advertising Create Sync Cancel */
+       //hci->commands[38] |= 0x04;    /* LE Periodic Advertising Terminate Sync */
+       //hci->commands[38] |= 0x08;    /* LE Add Device To Periodic Advertiser List */
+       //hci->commands[38] |= 0x10;    /* LE Remove Device From Periodic Advertiser List */
+       //hci->commands[38] |= 0x20;    /* LE Clear Periodic Advertiser List */
+       //hci->commands[38] |= 0x40;    /* LE Read Periodic Advertiser List Size */
+       //hci->commands[38] |= 0x80;    /* LE Read Transmit Power */
+       //hci->commands[39] |= 0x01;    /* LE Read RF Path Compensation */
+       //hci->commands[39] |= 0x02;    /* LE Write RF Path Compensation */
+       //hci->commands[39] |= 0x04;    /* LE Set Privacy Mode */
 
        memset(hci->features, 0, sizeof(hci->features));
        hci->features[4] |= 0x20;       /* BR/EDR Not Supported */
@@ -300,6 +339,15 @@ static void reset_defaults(struct bt_le *hci)
        //hci->le_event_mask[1] |= 0x01;        /* LE Generate DHKey Complete */
        //hci->le_event_mask[1] |= 0x02;        /* LE Enhanced Connection Complete */
        //hci->le_event_mask[1] |= 0x04;        /* LE Direct Advertising Report */
+       //hci->le_event_mask[1] |= 0x08;        /* LE PHY Update Complete */
+       //hci->le_event_mask[1] |= 0x10;        /* LE Extended Advertising Report */
+       //hci->le_event_mask[1] |= 0x20;        /* LE Periodic Advertising Sync Established */
+       //hci->le_event_mask[1] |= 0x40;        /* LE Periodic Advertising Report */
+       //hci->le_event_mask[1] |= 0x80;        /* LE Periodic Advertising Sync Lost */
+       //hci->le_event_mask[2] |= 0x01;        /* LE Extended Scan Timeout */
+       //hci->le_event_mask[2] |= 0x02;        /* LE Extended Advertising Set Terminated */
+       //hci->le_event_mask[2] |= 0x04;        /* LE Scan Request Received */
+       //hci->le_event_mask[2] |= 0x08;        /* LE Channel Selection Algorithm */
 
        hci->le_mtu = 64;
        hci->le_max_pkt = 1;
@@ -313,6 +361,15 @@ static void reset_defaults(struct bt_le *hci)
        hci->le_features[0] |= 0x20;    /* LE Data Packet Length Extension */
        hci->le_features[0] |= 0x40;    /* LL Privacy */
        hci->le_features[0] |= 0x80;    /* Extended Scanner Filter Policies */
+       hci->le_features[1] |= 0x01;    /* LE 2M PHY */
+       hci->le_features[1] |= 0x02;    /* Stable Modulation Index - Transmitter */
+       hci->le_features[1] |= 0x04;    /* Stable Modulation Index - Receiver */
+       hci->le_features[1] |= 0x08;    /* LE Coded PHY */
+       //hci->le_features[1] |= 0x10;  /* LE Extended Advertising */
+       //hci->le_features[1] |= 0x20;  /* LE Periodic Advertising */
+       hci->le_features[1] |= 0x40;    /* Channel Selection Algorithm #2 */
+       hci->le_features[1] |= 0x80;    /* LE Power Class 1 */
+       hci->le_features[2] |= 0x01;    /* Minimum Number of Used Channels Procedure */
 
        memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr));
 
@@ -369,6 +426,10 @@ static void reset_defaults(struct bt_le *hci)
        clear_resolv_list(hci);
        hci->le_resolv_enable = 0x00;
        hci->le_resolv_timeout = 0x0384;        /* 900 secs or 15 minutes */
+
+       hci->le_default_all_phys = DEFAULT_ALL_PHYS;
+       hci->le_default_tx_phys = DEFAULT_TX_PHYS;
+       hci->le_default_rx_phys = DEFAULT_RX_PHYS;
 }
 
 static void clear_scan_cache(struct bt_le *hci)
@@ -671,9 +732,9 @@ static void cmd_read_local_version(struct bt_le *hci,
        struct bt_hci_rsp_read_local_version rsp;
 
        rsp.status = BT_HCI_ERR_SUCCESS;
-       rsp.hci_ver = 0x08;
+       rsp.hci_ver = 0x09;
        rsp.hci_rev = cpu_to_le16(0x0000);
-       rsp.lmp_ver = 0x08;
+       rsp.lmp_ver = 0x09;
        rsp.manufacturer = cpu_to_le16(hci->manufacturer);
        rsp.lmp_subver = cpu_to_le16(0x0000);
 
@@ -1645,6 +1706,99 @@ static void cmd_le_read_max_data_length(struct bt_le *hci,
                                                        &rsp, sizeof(rsp));
 }
 
+static void cmd_le_read_phy(struct bt_le *hci, const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_read_phy *cmd = data;
+       struct bt_hci_rsp_le_read_phy rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.handle = cmd->handle;
+       rsp.tx_phy = 0x01;      /* LE 1M */
+       rsp.rx_phy = 0x01;      /* LE 1M */
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_PHY, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_default_phy(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_default_phy *cmd = data;
+       uint8_t status, tx_phys, rx_phys;
+       uint8_t phys_mask;
+
+       phys_mask = (true << 0) | ((!!(hci->le_features[1] & 0x01)) << 1)
+                               | ((!!(hci->le_features[1] & 0x08)) << 2);
+
+       if (cmd->all_phys > 0x03) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+               return;
+       }
+
+       /* Transmitter PHYs preferences */
+       if (!(cmd->all_phys & 0x01)) {
+               /* At least one preference bit shall be set to 1 */
+               if (!cmd->tx_phys) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+                       return;
+               }
+
+               if (cmd->tx_phys & ~phys_mask) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+                       return;
+               }
+
+               tx_phys = cmd->tx_phys;
+       } else
+               tx_phys = 0x00;
+
+       /* Transmitter PHYs preferences */
+       if (!(cmd->all_phys & 0x02)) {
+               /* At least one preference bit shall be set to 1 */
+               if (!cmd->rx_phys) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+                       return;
+               }
+
+               if (cmd->rx_phys & ~phys_mask) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_DEFAULT_PHY);
+                       return;
+               }
+
+               rx_phys = cmd->rx_phys;
+       } else
+               rx_phys = 0x00;
+
+       hci->le_default_all_phys = cmd->all_phys;
+       hci->le_default_tx_phys = tx_phys;
+       hci->le_default_rx_phys = rx_phys;
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_DEFAULT_PHY,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_set_phy(struct bt_le *hci, const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_phy *cmd = data;
+       struct bt_hci_evt_le_phy_update_complete evt;
+
+       cmd_status(hci, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_SET_PHY);
+
+       evt.status = BT_HCI_ERR_SUCCESS;
+       evt.handle = cmd->handle;
+       evt.tx_phy = 0x01;      /* LE 1M */
+       evt.rx_phy = 0x01;      /* LE 1M */
+
+       if (hci->le_event_mask[1] & 0x08)
+               le_meta_event(hci, BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE,
+                                                       &evt, sizeof(evt));
+}
+
 static const struct {
        uint16_t opcode;
        void (*func) (struct bt_le *hci, const void *data, uint8_t size);
@@ -1732,6 +1886,12 @@ static const struct {
                                cmd_le_set_resolv_timeout, 2, true },
        { BT_HCI_CMD_LE_READ_MAX_DATA_LENGTH,
                                cmd_le_read_max_data_length, 0, true },
+       { BT_HCI_CMD_LE_READ_PHY,
+                               cmd_le_read_phy, 2, true },
+       { BT_HCI_CMD_LE_SET_DEFAULT_PHY,
+                               cmd_le_set_default_phy, 3, true },
+       { BT_HCI_CMD_LE_SET_PHY,
+                               cmd_le_set_phy, 7, true },
 
        { }
 };
index a011e19..ab40596 100755 (executable)
@@ -76,6 +76,8 @@ struct GDBusProxy {
        void *prop_data;
        GDBusProxyFunction removed_func;
        void *removed_data;
+       DBusPendingCall *get_all_call;
+       gboolean pending;
 };
 
 struct prop_entry {
@@ -278,6 +280,17 @@ static void update_properties(GDBusProxy *proxy, DBusMessageIter *iter,
        }
 }
 
+static void proxy_added(GDBusClient *client, GDBusProxy *proxy)
+{
+       if (!proxy->pending)
+               return;
+
+       if (client->proxy_added)
+               client->proxy_added(proxy, client->user_data);
+
+       proxy->pending = FALSE;
+}
+
 static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
 {
        GDBusProxy *proxy = user_data;
@@ -286,6 +299,8 @@ static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
        DBusMessageIter iter;
        DBusError error;
 
+       g_dbus_client_ref(client);
+
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, reply) == TRUE) {
@@ -298,15 +313,13 @@ static void get_all_properties_reply(DBusPendingCall *call, void *user_data)
        update_properties(proxy, &iter, FALSE);
 
 done:
-       if (g_list_find(client->proxy_list, proxy) == NULL) {
-               if (client->proxy_added)
-                       client->proxy_added(proxy, client->user_data);
-
-               client->proxy_list = g_list_append(client->proxy_list, proxy);
-       }
+       proxy_added(client, proxy);
 
        dbus_message_unref(reply);
 
+       dbus_pending_call_unref(proxy->get_all_call);
+       proxy->get_all_call = NULL;
+
        g_dbus_client_unref(client);
 }
 
@@ -315,7 +328,9 @@ static void get_all_properties(GDBusProxy *proxy)
        GDBusClient *client = proxy->client;
        const char *service_name = client->service_name;
        DBusMessage *msg;
-       DBusPendingCall *call;
+
+       if (proxy->get_all_call)
+               return;
 
        msg = dbus_message_new_method_call(service_name, proxy->obj_path,
                                        DBUS_INTERFACE_PROPERTIES, "GetAll");
@@ -326,28 +341,24 @@ static void get_all_properties(GDBusProxy *proxy)
                                                        DBUS_TYPE_INVALID);
 
        if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
-                                                       &call, -1) == FALSE) {
+                                       &proxy->get_all_call, -1) == FALSE) {
                dbus_message_unref(msg);
                return;
        }
 
-       g_dbus_client_ref(client);
-
-       dbus_pending_call_set_notify(call, get_all_properties_reply,
-                                                       proxy, NULL);
-       dbus_pending_call_unref(call);
+       dbus_pending_call_set_notify(proxy->get_all_call,
+                                       get_all_properties_reply, proxy, NULL);
 
        dbus_message_unref(msg);
 }
 
-static GDBusProxy *proxy_lookup(GDBusClient *client, const char *path,
+static GDBusProxy *proxy_lookup(GList *list, const char *path,
                                                const char *interface)
 {
-       GList *list;
+       GList *l;
 
-       for (list = g_list_first(client->proxy_list); list;
-                                               list = g_list_next(list)) {
-               GDBusProxy *proxy = list->data;
+       for (l = g_list_first(list); l; l = g_list_next(l)) {
+               GDBusProxy *proxy = l->data;
 
                if (g_str_equal(proxy->interface, interface) == TRUE &&
                                g_str_equal(proxy->obj_path, path) == TRUE)
@@ -424,6 +435,9 @@ static GDBusProxy *proxy_new(GDBusClient *client, const char *path,
                                                        proxy->interface,
                                                        properties_changed,
                                                        proxy, NULL);
+       proxy->pending = TRUE;
+
+       client->proxy_list = g_list_append(client->proxy_list, proxy);
 
        return g_dbus_proxy_ref(proxy);
 }
@@ -435,6 +449,12 @@ static void proxy_free(gpointer data)
        if (proxy->client) {
                GDBusClient *client = proxy->client;
 
+               if (proxy->get_all_call != NULL) {
+                       dbus_pending_call_cancel(proxy->get_all_call);
+                       dbus_pending_call_unref(proxy->get_all_call);
+                       proxy->get_all_call = NULL;
+               }
+
                if (client->proxy_removed)
                        client->proxy_removed(proxy, client->user_data);
 
@@ -470,6 +490,27 @@ static void proxy_remove(GDBusClient *client, const char *path,
        }
 }
 
+static void start_service(GDBusProxy *proxy)
+{
+       GDBusClient *client = proxy->client;
+       const char *service_name = client->service_name;
+       dbus_uint32_t flags = 0;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+                                               DBUS_INTERFACE_DBUS,
+                                               "StartServiceByName");
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &service_name,
+                                       DBUS_TYPE_UINT32, &flags,
+                                       DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(client->dbus_conn, msg);
+       return;
+}
+
 GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
                                                        const char *interface)
 {
@@ -478,7 +519,7 @@ GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
        if (client == NULL)
                return NULL;
 
-       proxy = proxy_lookup(client, path, interface);
+       proxy = proxy_lookup(client->proxy_list, path, interface);
        if (proxy)
                return g_dbus_proxy_ref(proxy);
 
@@ -486,7 +527,14 @@ GDBusProxy *g_dbus_proxy_new(GDBusClient *client, const char *path,
        if (proxy == NULL)
                return NULL;
 
-       get_all_properties(proxy);
+       if (!client->connected) {
+               /* Force service to start */
+               start_service(proxy);
+               return g_dbus_proxy_ref(proxy);
+       }
+
+       if (!client->get_objects_call)
+               get_all_properties(proxy);
 
        return g_dbus_proxy_ref(proxy);
 }
@@ -509,6 +557,11 @@ void g_dbus_proxy_unref(GDBusProxy *proxy)
        if (__sync_sub_and_fetch(&proxy->ref_count, 1) > 0)
                return;
 
+       if (proxy->get_all_call != NULL) {
+               dbus_pending_call_cancel(proxy->get_all_call);
+               dbus_pending_call_unref(proxy->get_all_call);
+       }
+
        g_hash_table_destroy(proxy->prop_list);
 
        g_free(proxy->obj_path);
@@ -916,15 +969,15 @@ gboolean g_dbus_proxy_set_removed_watch(GDBusProxy *proxy,
        return TRUE;
 }
 
-static void refresh_properties(GDBusClient *client)
+static void refresh_properties(GList *list)
 {
-       GList *list;
+       GList *l;
 
-       for (list = g_list_first(client->proxy_list); list;
-                                               list = g_list_next(list)) {
+       for (l = g_list_first(list); l; l = g_list_next(l)) {
                GDBusProxy *proxy = list->data;
 
-               get_all_properties(proxy);
+               if (proxy->pending)
+                       get_all_properties(proxy);
         }
 }
 
@@ -939,22 +992,21 @@ static void parse_properties(GDBusClient *client, const char *path,
        if (g_str_equal(interface, DBUS_INTERFACE_PROPERTIES) == TRUE)
                return;
 
-       proxy = proxy_lookup(client, path, interface);
-       if (proxy) {
+       proxy = proxy_lookup(client->proxy_list, path, interface);
+       if (proxy && !proxy->pending) {
                update_properties(proxy, iter, FALSE);
                return;
        }
 
-       proxy = proxy_new(client, path, interface);
-       if (proxy == NULL)
-               return;
+       if (!proxy) {
+               proxy = proxy_new(client, path, interface);
+               if (proxy == NULL)
+                       return;
+       }
 
        update_properties(proxy, iter, FALSE);
 
-       if (client->proxy_added)
-               client->proxy_added(proxy, client->user_data);
-
-       client->proxy_list = g_list_append(client->proxy_list, proxy);
+       proxy_added(client, proxy);
 }
 
 static void parse_interfaces(GDBusClient *client, const char *path,
@@ -1103,6 +1155,8 @@ done:
        dbus_pending_call_unref(client->get_objects_call);
        client->get_objects_call = NULL;
 
+       refresh_properties(client->proxy_list);
+
        g_dbus_client_unref(client);
 }
 
@@ -1115,7 +1169,7 @@ static void get_managed_objects(GDBusClient *client)
 
        if ((!client->proxy_added && !client->proxy_removed) ||
                                                        !client->root_path) {
-               refresh_properties(client);
+               refresh_properties(client->proxy_list);
                return;
        }
 
@@ -1152,11 +1206,11 @@ static void service_connect(DBusConnection *conn, void *user_data)
 
        client->connected = TRUE;
 
+       get_managed_objects(client);
+
        if (client->connect_func)
                client->connect_func(conn, client->connect_data);
 
-       get_managed_objects(client);
-
        g_dbus_client_unref(client);
 }
 
index bd80060..bb2ac4c 100755 (executable)
@@ -1785,8 +1785,6 @@ static void process_properties_from_interface(struct generic_data *data,
        DBusMessageIter iter, dict, array;
        GSList *invalidated;
 
-       data->pending_prop = FALSE;
-
        if (iface->pending_prop == NULL)
                return;
 
@@ -1848,6 +1846,8 @@ static void process_property_changes(struct generic_data *data)
 {
        GSList *l;
 
+       data->pending_prop = FALSE;
+
        for (l = data->interfaces; l != NULL; l = l->next) {
                struct interface_data *iface = l->data;
 
index 6f7a952..ca3e871 100755 (executable)
@@ -154,6 +154,9 @@ gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len)
        GHashTableIter iter;
        gpointer key, value;
 
+       if (!apparam)
+               return 0;
+
        g_hash_table_iter_init(&iter, apparam->tags);
        while (g_hash_table_iter_next(&iter, &key, &value)) {
                struct apparam_tag *tag = value;
index db56ed0..cd5c131 100755 (executable)
@@ -228,7 +228,7 @@ gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, gsize len,
 }
 
 GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
-                                       guint8 first_hdr_id, va_list args)
+                                       guint first_hdr_id, va_list args)
 {
        GObexPacket *pkt;
 
@@ -246,7 +246,7 @@ GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
 }
 
 GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final,
-                                               guint8 first_hdr_id, ...)
+                                               guint first_hdr_id, ...)
 {
        GObexPacket *pkt;
        va_list args;
index d1007ea..1d94ccf 100755 (executable)
@@ -98,9 +98,9 @@ gboolean g_obex_packet_set_data(GObexPacket *pkt, const void *data, gsize len,
                                                GObexDataPolicy data_policy);
 const void *g_obex_packet_get_data(GObexPacket *pkt, gsize *len);
 GObexPacket *g_obex_packet_new(guint8 opcode, gboolean final,
-                                               guint8 first_hdr_id, ...);
+                                               guint first_hdr_id, ...);
 GObexPacket *g_obex_packet_new_valist(guint8 opcode, gboolean final,
-                                       guint8 first_hdr_id, va_list args);
+                                       guint first_hdr_id, va_list args);
 void g_obex_packet_free(GObexPacket *pkt);
 
 GObexPacket *g_obex_packet_decode(const void *data, gsize len,
index a5115bf..bc99306 100755 (executable)
@@ -296,7 +296,7 @@ guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
 
 guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...)
+                       GError **err, guint first_hdr_id, ...)
 {
        GObexPacket *req;
        va_list args;
@@ -414,7 +414,7 @@ done:
 guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
                        GObexDataConsumer data_func, GObexFunc complete_func,
                        gpointer user_data, GError **err,
-                       guint8 first_hdr_id, ...)
+                       guint first_hdr_id, ...)
 {
        struct transfer *transfer;
        va_list args;
@@ -471,7 +471,7 @@ guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
 
 guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...)
+                       GError **err, guint first_hdr_id, ...)
 {
        struct transfer *transfer;
        GObexPacket *req;
@@ -617,7 +617,7 @@ guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
 
 guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...)
+                       GError **err, guint first_hdr_id, ...)
 {
        GObexPacket *rsp;
        va_list args;
index d0b1e01..84e6100 100644 (file)
@@ -858,7 +858,7 @@ immediate_completion:
 }
 
 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
-                                               guint8 first_hdr_type, ...)
+                                               guint first_hdr_type, ...)
 {
        GObexPacket *rsp;
        va_list args;
@@ -1561,7 +1561,7 @@ void g_obex_io_shutdown(GObex *obex)
 }
 #endif
 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
-                                       GError **err, guint8 first_hdr_id, ...)
+                                       GError **err, guint first_hdr_id, ...)
 {
        GObexPacket *req;
        struct connect_data data;
index 827dc3b..a33fc60 100644 (file)
@@ -51,7 +51,7 @@ gboolean g_obex_cancel_req(GObex *obex, guint req_id,
                                                gboolean remove_callback);
 
 gboolean g_obex_send_rsp(GObex *obex, guint8 rspcode, GError **err,
-                                               guint8 first_hdr_type, ...);
+                                               guint first_hdr_type, ...);
 
 void g_obex_set_disconnect_function(GObex *obex, GObexFunc func,
                                                        gpointer user_data);
@@ -73,7 +73,7 @@ void g_obex_unref(GObex *obex);
 /* High level client functions */
 
 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
-                               GError **err, guint8 first_hdr_id, ...);
+                               GError **err, guint first_hdr_id, ...);
 
 guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
                                                                GError **err);
@@ -102,7 +102,7 @@ guint g_obex_abort(GObex *obex, GObexResponseFunc func, gpointer user_data,
 
 guint g_obex_put_req(GObex *obex, GObexDataProducer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...);
+                       GError **err, guint first_hdr_id, ...);
 
 guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
                        GObexDataProducer data_func, GObexFunc complete_func,
@@ -110,7 +110,7 @@ guint g_obex_put_req_pkt(GObex *obex, GObexPacket *req,
 
 guint g_obex_get_req(GObex *obex, GObexDataConsumer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...);
+                       GError **err, guint first_hdr_id, ...);
 
 guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
                        GObexDataConsumer data_func, GObexFunc complete_func,
@@ -119,11 +119,11 @@ guint g_obex_get_req_pkt(GObex *obex, GObexPacket *req,
 guint g_obex_put_rsp(GObex *obex, GObexPacket *req,
                        GObexDataConsumer data_func, GObexFunc complete_func,
                        gpointer user_data, GError **err,
-                       guint8 first_hdr_id, ...);
+                       guint first_hdr_id, ...);
 
 guint g_obex_get_rsp(GObex *obex, GObexDataProducer data_func,
                        GObexFunc complete_func, gpointer user_data,
-                       GError **err, guint8 first_hdr_id, ...);
+                       GError **err, guint first_hdr_id, ...);
 
 guint g_obex_get_rsp_pkt(GObex *obex, GObexPacket *rsp,
                        GObexDataProducer data_func, GObexFunc complete_func,
index 4dd92b8..0cdb471 100755 (executable)
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -667,6 +667,7 @@ static hci_map ver_map[] = {
        { "4.0",        0x06 },
        { "4.1",        0x07 },
        { "4.2",        0x08 },
+       { "5.0",        0x09 },
        { NULL }
 };
 
index cc10e9f..f586eb5 100755 (executable)
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -176,7 +176,6 @@ extern "C" {
 #define AV_REMOTE_TARGET_PROFILE_ID    AV_REMOTE_TARGET_SVCLASS_ID
 #define ADVANCED_AUDIO_PROFILE_ID      ADVANCED_AUDIO_SVCLASS_ID
 #define AV_REMOTE_PROFILE_ID           AV_REMOTE_SVCLASS_ID
-#define VIDEO_CONF_PROFILE_ID          VIDEO_CONF_SVCLASS_ID
 #define INTERCOM_PROFILE_ID            INTERCOM_SVCLASS_ID
 #define FAX_PROFILE_ID                 FAX_SVCLASS_ID
 #define HEADSET_AGW_PROFILE_ID         HEADSET_AGW_SVCLASS_ID
index e43ba70..1a67fba 100755 (executable)
@@ -133,6 +133,7 @@ extern "C" {
 #define GATT_CHARAC_RECONNECTION_ADDRESS               0x2A03
 #define GATT_CHARAC_PERIPHERAL_PREF_CONN               0x2A04
 #define GATT_CHARAC_SERVICE_CHANGED                    0x2A05
+#define GATT_CHARAC_BATTERY_LEVEL                      0x2A19
 #define GATT_CHARAC_SYSTEM_ID                          0x2A23
 #define GATT_CHARAC_MODEL_NUMBER_STRING                        0x2A24
 #define GATT_CHARAC_SERIAL_NUMBER_STRING               0x2A25
@@ -156,6 +157,16 @@ extern "C" {
 #define GATT_EXTERNAL_REPORT_REFERENCE                 0x2907
 #define GATT_REPORT_REFERENCE                          0x2908
 
+/* GATT Mesh Services */
+#define MESH_PROV_SVC_UUID     "00001827-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_SVC_UUID    "00001828-0000-1000-8000-00805f9b34fb"
+
+/* GATT Mesh Characteristic Types */
+#define MESH_PROVISIONING_DATA_IN                      0x2ADB
+#define MESH_PROVISIONING_DATA_OUT                     0x2ADC
+#define MESH_PROXY_DATA_IN                             0x2ADD
+#define MESH_PROXY_DATA_OUT                            0x2ADE
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 /* GATT Service UUIDs : Defined  by SIG */
 #define GATT_IPSP_UUID         0x1820
diff --git a/mesh/README b/mesh/README
new file mode 100644 (file)
index 0000000..ea561ef
--- /dev/null
@@ -0,0 +1,26 @@
+MeshCtl - BlueZ GATT based Bluetooth Mesh Provisioner
+******************************************
+
+Copyright (C) 2017  Intel Corporation. All rights reserved.
+
+Compilation and installation
+============================
+
+In addition to main BlueZ requirements, MeshCtl needs the following:
+       - JSON library
+
+Configuration and options
+=========================
+
+       --enable-mesh
+
+               Build meshctl and other Bluetooth Mesh based tools and utils
+
+Information
+===========
+
+Mailing lists:
+       linux-bluetooth@vger.kernel.org
+
+For additional information about the project visit BlueZ web site:
+       http://www.bluez.org
diff --git a/mesh/agent.c b/mesh/agent.c
new file mode 100644 (file)
index 0000000..b8a14f0
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include <lib/bluetooth.h>
+
+#include "src/shared/shell.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+
+struct input_request {
+       oob_type_t type;
+       uint16_t len;
+       agent_input_cb cb;
+       void *user_data;
+};
+
+static struct input_request pending_request = {NONE, 0, NULL, NULL};
+
+bool agent_completion(void)
+{
+       if (pending_request.type == NONE)
+               return false;
+
+       return true;
+}
+
+static void reset_input_request(void)
+{
+       pending_request.type = NONE;
+       pending_request.len = 0;
+       pending_request.cb = NULL;
+       pending_request.user_data = NULL;
+}
+
+static void response_hexadecimal(const char *input, void *user_data)
+{
+       uint8_t buf[MAX_HEXADECIMAL_OOB_LEN];
+
+       if (!str2hex(input, strlen(input), buf, pending_request.len) ) {
+               bt_shell_printf("Incorrect input: expecting %d hex octets\n",
+                         pending_request.len);
+               return;
+       }
+
+       if (pending_request.cb)
+               pending_request.cb(HEXADECIMAL, buf, pending_request.len,
+                                       pending_request.user_data);
+
+       reset_input_request();
+}
+
+static void response_decimal(const char *input, void *user_data)
+{
+       uint8_t buf[DECIMAL_OOB_LEN];
+
+       if (strlen(input) > pending_request.len)
+               return;
+
+       bt_put_be32(atoi(input), buf);
+
+       if (pending_request.cb)
+               pending_request.cb(DECIMAL, buf, DECIMAL_OOB_LEN,
+                                       pending_request.user_data);
+
+       reset_input_request();
+}
+
+static void response_ascii(const char *input, void *user_data)
+{
+       if (pending_request.cb)
+               pending_request.cb(ASCII, (uint8_t *) input, strlen(input),
+                                       pending_request.user_data);
+
+       reset_input_request();
+}
+
+static bool request_hexadecimal(uint16_t len)
+{
+       if (len > MAX_HEXADECIMAL_OOB_LEN)
+               return false;
+
+       bt_shell_printf("Request hexadecimal key (hex %d octets)\n", len);
+       bt_shell_prompt_input("mesh", "Enter key (hex number):", response_hexadecimal,
+                                                               NULL);
+
+       return true;
+}
+
+static uint32_t power_ten(uint8_t power)
+{
+       uint32_t ret = 1;
+
+       while (power--)
+               ret *= 10;
+
+       return ret;
+}
+
+static bool request_decimal(uint16_t len)
+{
+       bt_shell_printf("Request decimal key (0 - %d)\n", power_ten(len) - 1);
+       bt_shell_prompt_input("mesh", "Enter Numeric key:", response_decimal, NULL);
+
+       return true;
+}
+
+static bool request_ascii(uint16_t len)
+{
+       if (len > MAX_ASCII_OOB_LEN)
+               return false;
+
+       bt_shell_printf("Request ASCII key (max characters %d)\n", len);
+       bt_shell_prompt_input("mesh", "Enter key (ascii string):", response_ascii,
+                                                                       NULL);
+
+       return true;
+}
+
+bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,
+                               void *user_data)
+{
+       bool result;
+
+       if (pending_request.type != NONE)
+               return FALSE;
+
+       switch (type) {
+       case HEXADECIMAL:
+               result = request_hexadecimal(max_len);
+               break;
+       case DECIMAL:
+               result = request_decimal(max_len);
+               break;
+       case ASCII:
+               result = request_ascii(max_len);
+               break;
+       case NONE:
+       case OUTPUT:
+       default:
+               return false;
+       };
+
+       if (result) {
+               pending_request.type = type;
+               pending_request.len = max_len;
+               pending_request.cb = cb;
+               pending_request.user_data = user_data;
+
+               return true;
+       }
+
+       return false;
+}
+
+static void response_output(const char *input, void *user_data)
+{
+       reset_input_request();
+}
+
+bool agent_output_request(const char* str)
+{
+       if (pending_request.type != NONE)
+               return false;
+
+       pending_request.type = OUTPUT;
+       bt_shell_prompt_input("mesh", str, response_output, NULL);
+       return true;
+}
+
+void agent_output_request_cancel(void)
+{
+       if (pending_request.type != OUTPUT)
+               return;
+       pending_request.type = NONE;
+       bt_shell_release_prompt("");
+}
old mode 100755 (executable)
new mode 100644 (file)
similarity index 55%
rename from android/hog.h
rename to mesh/agent.h
index 2a9b899..9db4321
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2017  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
  *
  */
 
-struct bt_hog;
+#define MAX_HEXADECIMAL_OOB_LEN        128
+#define DECIMAL_OOB_LEN                4
+#define MAX_ASCII_OOB_LEN              16
 
-struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
-                                       uint16_t product, uint16_t version,
-                                       void *primary);
+typedef enum {
+       NONE,
+       HEXADECIMAL,
+       DECIMAL,
+       ASCII,
+       OUTPUT,
+} oob_type_t;
 
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
-                                       uint16_t product, uint16_t version,
-                                       void *primary);
+typedef void (*agent_input_cb)(oob_type_t type, void *input, uint16_t len,
+                                       void *user_data);
+bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,
+                               void *user_data);
 
-struct bt_hog *bt_hog_ref(struct bt_hog *hog);
-void bt_hog_unref(struct bt_hog *hog);
-
-bool bt_hog_attach(struct bt_hog *hog, void *gatt);
-void bt_hog_detach(struct bt_hog *hog);
-
-int bt_hog_set_control_point(struct bt_hog *hog, bool suspend);
-int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type);
+bool agent_output_request(const char* str);
+void agent_output_request_cancel(void);
+bool agent_completion(void);
+bool agent_input(const char *input);
+void agent_release(void);
diff --git a/mesh/config-client.c b/mesh/config-client.c
new file mode 100644 (file)
index 0000000..f280441
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/config-model.h"
+
+#define MIN_COMPOSITION_LEN 16
+
+static bool client_msg_recvd(uint16_t src, uint8_t *data,
+                               uint16_t len, void *user_data)
+{
+       uint32_t opcode;
+       struct mesh_node *node;
+       uint16_t app_idx, net_idx, addr;
+       uint32_t mod_id;
+       uint16_t primary;
+       uint16_t ele_addr;
+       uint8_t ele_idx;
+       struct mesh_publication pub;
+       int n;
+       uint16_t i;
+
+       if (mesh_opcode_get(data, len, &opcode, &n)) {
+               len -= n;
+               data += n;
+       } else
+               return false;
+
+       if (IS_UNICAST(src)) {
+               node = node_find_by_addr(src);
+       } else
+               node = NULL;
+
+       if (!node)
+               return false;
+
+       primary = node_get_primary(node);
+       if (primary != src)
+               return false;
+
+       switch (opcode & ~OP_UNRELIABLE) {
+       default:
+               return false;
+
+       case OP_DEV_COMP_STATUS:
+               if (len < MIN_COMPOSITION_LEN || !node)
+                       break;
+               if (node_parse_composition(node, data, len)) {
+                       if (!prov_db_add_node_composition(node, data, len))
+                               break;
+               }
+
+               if (node_get_composition(node))
+                       prov_db_print_node_composition(node);
+               break;
+
+       case OP_APPKEY_STATUS:
+               if (len != 4)
+                       break;
+
+               bt_shell_printf("Node %4.4x AppKey Status %s\n", src,
+                                               mesh_status_str(data[0]));
+               net_idx = get_le16(data + 1) & 0xfff;
+               app_idx = get_le16(data + 2) >> 4;
+
+               bt_shell_printf("\tNetKey %3.3x, AppKey %3.3x\n", net_idx, app_idx);
+
+               if (data[0] != MESH_STATUS_SUCCESS &&
+                               data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
+                               node_app_key_delete(node, net_idx, app_idx))
+                       prov_db_node_keys(node, node_get_app_keys(node),
+                                                               "appKeys");
+               break;
+
+       case OP_NETKEY_STATUS:
+               if (len != 3)
+                       break;
+
+               bt_shell_printf("Node %4.4x NetKey Status %s\n", src,
+                                               mesh_status_str(data[0]));
+               net_idx = get_le16(data + 1) & 0xfff;
+
+               bt_shell_printf("\tNetKey %3.3x\n", net_idx);
+
+               if (data[0] != MESH_STATUS_SUCCESS &&
+                               data[0] != MESH_STATUS_IDX_ALREADY_STORED &&
+                                       node_net_key_delete(node, net_idx))
+                       prov_db_node_keys(node, node_get_net_keys(node),
+                                                               "netKeys");
+               break;
+
+       case OP_MODEL_APP_STATUS:
+               if (len != 7 && len != 9)
+                       break;
+
+               bt_shell_printf("Node %4.4x Model App Status %s\n", src,
+                                               mesh_status_str(data[0]));
+               addr = get_le16(data + 1);
+               app_idx = get_le16(data + 3);
+
+               bt_shell_printf("\tElement %4.4x AppIdx %3.3x\n ", addr, app_idx);
+
+               if (len == 7) {
+                       mod_id = get_le16(data + 5);
+                       bt_shell_printf("ModelId %4.4x\n", mod_id);
+                       mod_id = 0xffff0000 | mod_id;
+               } else {
+                       mod_id = get_le16(data + 7);
+                       bt_shell_printf("ModelId %4.4x %4.4x\n", get_le16(data + 5),
+                                                                       mod_id);
+                       mod_id = get_le16(data + 5) << 16 | mod_id;
+               }
+
+               if (data[0] == MESH_STATUS_SUCCESS &&
+                       node_add_binding(node, addr - src, mod_id, app_idx))
+                       prov_db_add_binding(node, addr - src, mod_id, app_idx);
+               break;
+
+       case OP_NODE_IDENTITY_STATUS:
+               if (len != 4)
+                       return true;
+               bt_shell_printf("Network index 0x%04x has "
+                               "Node Identity state 0x%02x %s\n",
+                               get_le16(data + 1), data[3],
+                               mesh_status_str(data[0]));
+               break;
+
+       case OP_CONFIG_RELAY_STATUS:
+               if (len != 2)
+                       return true;
+               bt_shell_printf("Node %4.4x Relay state: 0x%02x"
+                               " count: %d steps: %d\n",
+                               src, data[0], data[1]>>5, data[1] & 0x1f);
+               break;
+
+       case OP_CONFIG_PROXY_STATUS:
+               if (len != 1)
+                       return true;
+               bt_shell_printf("Node %4.4x Proxy state: 0x%02x\n",
+                               src, data[0]);
+               break;
+
+       case OP_CONFIG_DEFAULT_TTL_STATUS:
+               if (len != 1)
+                       return true;
+               bt_shell_printf("Node %4.4x Default TTL %d\n", src, data[0]);
+               if (node_set_default_ttl (node, data[0]))
+                       prov_db_node_set_ttl(node, data[0]);
+               break;
+
+       case OP_CONFIG_MODEL_PUB_STATUS:
+               if (len != 12 && len != 14)
+                       return true;
+
+               bt_shell_printf("\nSet publication for node %4.4x status: %s\n",
+                               src, data[0] == MESH_STATUS_SUCCESS ?
+                               "Success" : mesh_status_str(data[0]));
+
+               if (data[0] != MESH_STATUS_SUCCESS)
+                       return true;
+
+               ele_addr = get_le16(data + 1);
+               mod_id = get_le16(data + 10);
+               if (len == 14)
+                       mod_id = (mod_id << 16)  | get_le16(data + 12);
+               else
+                       mod_id |= 0xffff0000;
+
+               pub.u.addr16 = get_le16(data + 3);
+               pub.app_idx = get_le16(data + 5);
+               pub.ttl = data[7];
+               pub.period = data[8];
+               n = (data[8] & 0x3f);
+               bt_shell_printf("Publication address: 0x%04x\n", pub.u.addr16);
+               switch (data[8] >> 6) {
+               case 0:
+                       bt_shell_printf("Period: %d ms\n", n * 100);
+                       break;
+               case 2:
+                       n *= 10;
+                       /* fall through */
+               case 1:
+                       bt_shell_printf("Period: %d sec\n", n);
+                       break;
+               case 3:
+                       bt_shell_printf("Period: %d min\n", n * 10);
+                       break;
+               }
+
+               pub.retransmit = data[9];
+               bt_shell_printf("Retransmit count: %d\n", data[9] >> 5);
+               bt_shell_printf("Retransmit Interval Steps: %d\n",
+                               data[9] & 0x1f);
+
+               ele_idx = ele_addr - node_get_primary(node);
+
+               /* Local configuration is saved by server */
+               if (node == node_get_local_node())
+                       break;
+
+               if (node_model_pub_set(node, ele_idx, mod_id, &pub))
+                       prov_db_node_set_model_pub(node, ele_idx, mod_id,
+                                    node_model_pub_get(node, ele_idx, mod_id));
+               break;
+
+       /* Per Mesh Profile 4.3.2.19 */
+       case OP_CONFIG_MODEL_SUB_STATUS:
+               bt_shell_printf("\nSubscription changed"
+                               " for node %4.4x status: %s\n", src,
+                               data[0] == MESH_STATUS_SUCCESS ? "Success" :
+                                               mesh_status_str(data[0]));
+
+               if (data[0] != MESH_STATUS_SUCCESS)
+                       return true;
+
+               bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+               bt_shell_printf("Subscr Addr:\t%4.4x\n", get_le16(data + 3));
+               bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 5));
+               break;
+
+               /* TODO */
+               /* Save subscription info in database */
+
+       /* Per Mesh Profile 4.3.2.27 */
+       case OP_CONFIG_MODEL_SUB_LIST:
+
+               bt_shell_printf("\nSubscription list for node %4.4x "
+                               "length: %u status: %s\n", src, len,
+                               data[0] == MESH_STATUS_SUCCESS ? "Success" :
+                                               mesh_status_str(data[0]));
+
+               if (data[0] != MESH_STATUS_SUCCESS)
+                       return true;
+
+               bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+               bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+               for (i = 5; i < len; i += 2)
+                       bt_shell_printf("Subscr Addr:\t%4.4x\n",
+                                       get_le16(data + i));
+               break;
+
+       /* Per Mesh Profile 4.3.2.50 */
+       case OP_MODEL_APP_LIST:
+               bt_shell_printf("\nModel App Key list for node %4.4x "
+                               "length: %u status: %s\n", src, len,
+                               data[0] == MESH_STATUS_SUCCESS ? "Success" :
+                                               mesh_status_str(data[0]));
+
+               if (data[0] != MESH_STATUS_SUCCESS)
+                       return true;
+
+               bt_shell_printf("Element Addr:\t%4.4x\n", get_le16(data + 1));
+               bt_shell_printf("Model ID:\t%4.4x\n", get_le16(data + 3));
+
+               for (i = 5; i < len; i += 2)
+                       bt_shell_printf("Model App Key:\t%4.4x\n",
+                                       get_le16(data + i));
+               break;
+
+       /* Per Mesh Profile 4.3.2.63 */
+       case OP_CONFIG_HEARTBEAT_PUB_STATUS:
+               bt_shell_printf("\nSet heartbeat for node %4.4x status: %s\n",
+                               src,
+                               data[0] == MESH_STATUS_SUCCESS ? "Success" :
+                                               mesh_status_str(data[0]));
+
+               if (data[0] != MESH_STATUS_SUCCESS)
+                       return true;
+
+               bt_shell_printf("Destination:\t%4.4x\n", get_le16(data + 1));
+               bt_shell_printf("Count:\t\t%2.2x\n", data[3]);
+               bt_shell_printf("Period:\t\t%2.2x\n", data[4]);
+               bt_shell_printf("TTL:\t\t%2.2x\n", data[5]);
+               bt_shell_printf("Features:\t%4.4x\n", get_le16(data + 6));
+               bt_shell_printf("Net_Idx:\t%4.4x\n", get_le16(data + 8));
+               break;
+       }
+
+       return true;
+}
+
+static uint32_t target;
+static uint32_t parms[8];
+
+static uint32_t read_input_parameters(int argc, char *argv[])
+{
+       uint32_t i;
+
+       if (!argc)
+               return 0;
+
+       --argc;
+       ++argv;
+
+       if (!argc || argv[0][0] == '\0')
+               return 0;
+
+       memset(parms, 0xff, sizeof(parms));
+
+       for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc;
+                                                                       i++) {
+               sscanf(argv[i], "%x", &parms[i]);
+               if (parms[i] == 0xffffffff)
+                       break;
+       }
+
+       return i;
+}
+
+static void cmd_set_node(int argc, char *argv[])
+{
+       uint32_t dst;
+       char *end;
+
+       dst = strtol(argv[1], &end, 16);
+       if (end != (argv[1] + 4)) {
+               bt_shell_printf("Bad unicast address %s: "
+                               "expected format 4 digit hex\n", argv[1]);
+               target = UNASSIGNED_ADDRESS;
+       } else {
+               bt_shell_printf("Configuring node %4.4x\n", dst);
+               target = dst;
+               set_menu_prompt("config", argv[1]);
+       }
+}
+
+static bool config_send(uint8_t *buf, uint16_t len)
+{
+       struct mesh_node *node = node_get_local_node();
+       uint16_t primary;
+
+       if(!node)
+               return false;
+
+       primary = node_get_primary(node);
+       if (target != primary)
+               return net_access_layer_send(DEFAULT_TTL, primary,
+                                               target, APP_IDX_DEV, buf, len);
+
+       node_local_data_handler(primary, target, node_get_iv_index(node),
+                               node_get_sequence_number(node), APP_IDX_DEV,
+                               buf, len);
+       return true;
+
+}
+
+static void cmd_default(uint32_t opcode)
+{
+       uint16_t n;
+       uint8_t msg[32];
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(opcode, msg);
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send command (opcode 0x%x)\n",
+                                                               opcode);
+}
+
+static void cmd_get_composition(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       node = node_find_by_addr(target);
+
+       if (!node)
+               return;
+
+       n = mesh_opcode_set(OP_DEV_COMP_GET, msg);
+
+       /* By default, use page 0 */
+       msg[n++] = (read_input_parameters(argc, argv) == 1) ? parms[0] : 0;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"GET NODE COMPOSITION\"\n");
+}
+
+static void cmd_net_key(int argc, char *argv[], uint32_t opcode)
+{
+       uint16_t n;
+       uint8_t msg[32];
+       uint16_t net_idx;
+       uint8_t *key;
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(opcode, msg);
+
+       if (read_input_parameters(argc, argv) != 1) {
+               bt_shell_printf("Bad arguments %s\n", argv[1]);
+               return;
+       }
+
+       node = node_find_by_addr(target);
+       if (!node) {
+               bt_shell_printf("Node %4.4x\n not found", target);
+               return;
+       }
+
+       net_idx = parms[0];
+
+       if (opcode != OP_NETKEY_DELETE) {
+
+               key = keys_net_key_get(net_idx, true);
+               if (!key) {
+                       bt_shell_printf("Network key with index %4.4x not found\n",
+                                                               net_idx);
+                       return;
+               }
+
+               put_le16(net_idx, &msg[n]);
+               n += 2;
+
+               memcpy(msg + n, key, 16);
+               n += 16;
+       }
+
+       if (!config_send(msg, n)) {
+               bt_shell_printf("Failed to send \"%s NET KEY\"\n",
+                               opcode == OP_NETKEY_ADD ? "ADD" : "DEL");
+               return;
+       }
+
+       if (opcode != OP_NETKEY_DELETE) {
+               if (node_net_key_add(node, net_idx))
+                       prov_db_node_keys(node, node_get_net_keys(node),
+                                                               "netKeys");
+       } else {
+               if (node_net_key_delete(node, net_idx))
+                       prov_db_node_keys(node, node_get_net_keys(node),
+                                                               "netKeys");
+       }
+
+}
+
+static void cmd_add_net_key(int argc, char *argv[])
+{
+       cmd_net_key(argc, argv, OP_NETKEY_ADD);
+}
+
+static void cmd_del_net_key(int argc, char *argv[])
+{
+       cmd_net_key(argc, argv, OP_NETKEY_DELETE);
+}
+
+static void cmd_app_key(int argc, char *argv[], uint32_t opcode)
+{
+       uint16_t n;
+       uint8_t msg[32];
+       uint16_t net_idx;
+       uint16_t app_idx;
+       uint8_t *key;
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       if (read_input_parameters(argc, argv) != 1) {
+               bt_shell_printf("Bad arguments %s\n", argv[1]);
+               return;
+       }
+
+       node = node_find_by_addr(target);
+       if (!node) {
+               bt_shell_printf("Node %4.4x\n not found", target);
+               return;
+       }
+
+       n = mesh_opcode_set(opcode, msg);
+
+       app_idx = parms[0];
+       net_idx = keys_app_key_get_bound(app_idx);
+       if (net_idx == NET_IDX_INVALID) {
+               bt_shell_printf("App key with index %4.4x not found\n", app_idx);
+               return;
+       }
+
+       msg[n++] = net_idx & 0xf;
+       msg[n++] = ((net_idx >> 8) & 0xf) |
+               ((app_idx << 4) & 0xf0);
+       msg[n++] = app_idx >> 4;
+
+       if (opcode != OP_APPKEY_DELETE) {
+               key = keys_app_key_get(app_idx, true);
+               if (!key) {
+                       bt_shell_printf("App key %4.4x not found\n", net_idx);
+                       return;
+               }
+
+               memcpy(msg + n, key, 16);
+               n += 16;
+       }
+
+       if (!config_send(msg, n)) {
+               bt_shell_printf("Failed to send \"ADD %s KEY\"\n",
+                               opcode == OP_APPKEY_ADD ? "ADD" : "DEL");
+               return;
+       }
+
+       if (opcode != OP_APPKEY_DELETE) {
+               if (node_app_key_add(node, app_idx))
+                       prov_db_node_keys(node, node_get_app_keys(node),
+                                                               "appKeys");
+       } else {
+               if (node_app_key_delete(node, net_idx, app_idx))
+                       prov_db_node_keys(node, node_get_app_keys(node),
+                                                               "appKeys");
+       }
+}
+
+static void cmd_add_app_key(int argc, char *argv[])
+{
+       cmd_app_key(argc, argv, OP_APPKEY_ADD);
+}
+
+static void cmd_del_app_key(int argc, char *argv[])
+{
+       cmd_app_key(argc, argv, OP_APPKEY_DELETE);
+}
+
+static bool verify_config_target(uint32_t dst)
+{
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(dst)) {
+               bt_shell_printf("Destination not set\n");
+               return false;
+       }
+
+       node = node_find_by_addr(dst);
+       if (!node) {
+               bt_shell_printf("Node with unicast address %4.4x unknown\n", dst);
+               return false;
+       }
+
+       if (!node_get_composition(node)) {
+               bt_shell_printf("Node composition for %4.4x unknown\n", dst);
+               return false;
+       }
+
+       return true;
+}
+
+static void cmd_bind(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 3 && parm_cnt != 4) {
+               bt_shell_printf("Bad arguments\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_MODEL_APP_BIND, msg);
+
+       put_le16(target + parms[0], msg + n);
+       n += 2;
+       put_le16(parms[1], msg + n);
+       n += 2;
+       if (parm_cnt == 4) {
+               put_le16(parms[3], msg + n);
+               put_le16(parms[2], msg + n + 2);
+               n += 4;
+       } else {
+               put_le16(parms[2], msg + n);
+               n += 2;
+       }
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"MODEL APP BIND\"\n");
+}
+
+static void cmd_set_ident(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[2 + 3 + 4];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       n = mesh_opcode_set(OP_NODE_IDENTITY_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 2) {
+               bt_shell_printf("bad arguments\n");
+               return;
+       }
+
+       put_le16(parms[0], msg + n);
+       n += 2;
+       msg[n++] = parms[1];
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET IDENTITY\"\n");
+}
+
+static void cmd_get_ident(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[2 + 2 + 4];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       n = mesh_opcode_set(OP_NODE_IDENTITY_GET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 1) {
+               bt_shell_printf("bad arguments\n");
+               return;
+       }
+
+       put_le16(parms[0], msg + n);
+       n += 2;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"GET IDENTITY\"\n");
+}
+
+static void cmd_set_proxy(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[2 + 1 + 4];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       n = mesh_opcode_set(OP_CONFIG_PROXY_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 1) {
+               bt_shell_printf("bad arguments");
+               return;
+       }
+
+       msg[n++] = parms[0];
+       msg[n++] = parms[1];
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET PROXY\"\n");
+}
+
+static void cmd_get_proxy(int argc, char *argv[])
+{
+       cmd_default(OP_CONFIG_PROXY_GET);
+}
+
+static void cmd_set_relay(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[2 + 2 + 4];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       n = mesh_opcode_set(OP_CONFIG_RELAY_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 3) {
+               bt_shell_printf("bad arguments\n");
+               return;
+       }
+
+       msg[n++] = parms[0];
+       msg[n++] = (parms[1] << 5) | parms[2];
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET RELAY\"\n");
+}
+
+static void cmd_get_relay(int argc, char *argv[])
+{
+       cmd_default(OP_CONFIG_RELAY_GET);
+}
+
+static void cmd_set_ttl(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+       uint8_t ttl;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt) {
+               ttl = parms[0] & TTL_MASK;
+       } else
+               ttl = node_get_default_ttl(node_get_local_node());
+
+       msg[n++] = ttl;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET_DEFAULT TTL\"\n");
+}
+
+static void cmd_set_pub(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (!verify_config_target(target))
+               return;
+
+       n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 6 && parm_cnt != 7) {
+               bt_shell_printf("Bad arguments\n");
+               return;
+       }
+
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Publish address */
+       put_le16(parms[1], msg + n);
+       n += 2;
+       /* App key index + credential (set to 0) */
+       put_le16(parms[2], msg + n);
+       n += 2;
+       /* TTL */
+       msg[n++] = DEFAULT_TTL;
+       /* Publish period  step count and step resolution */
+       msg[n++] = parms[3];
+       /* Publish retransmit count & interval steps */
+       msg[n++] = parms[4];
+       /* Model Id */
+       if (parm_cnt == 7) {
+               put_le16(parms[6], msg + n);
+               put_le16(parms[5], msg + n + 2);
+               n += 4;
+       } else {
+               put_le16(parms[5], msg + n);
+               n += 2;
+       }
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET MODEL PUBLICATION\"\n");
+}
+
+static void cmd_get_pub(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_CONFIG_MODEL_PUB_GET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 2 && parm_cnt != 3) {
+               bt_shell_printf("Bad arguments: %s\n", argv[1]);
+               return;
+       }
+
+       /* Element Address */
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Model Id */
+       if (parm_cnt == 3) {
+               put_le16(parms[2], msg + n);
+               put_le16(parms[1], msg + n + 2);
+               n += 4;
+       } else {
+               put_le16(parms[1], msg + n);
+               n += 2;
+       }
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"GET MODEL PUBLICATION\"\n");
+}
+
+static void cmd_sub_add(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_ADD, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 3) {
+               bt_shell_printf("Bad arguments: %s\n", argv[1]);
+               return;
+       }
+
+       /* Per Mesh Profile 4.3.2.19 */
+       /* Element Address */
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Subscription Address */
+       put_le16(parms[1], msg + n);
+       n += 2;
+       /* SIG Model ID */
+       put_le16(parms[2], msg + n);
+       n += 2;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"ADD SUBSCRIPTION\"\n");
+}
+
+static void cmd_sub_get(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_CONFIG_MODEL_SUB_GET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 2) {
+               bt_shell_printf("Bad arguments: %s\n", argv[1]);
+               return;
+       }
+
+       /* Per Mesh Profile 4.3.2.27 */
+       /* Element Address */
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Model ID */
+       put_le16(parms[1], msg + n);
+       n += 2;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"GET SUB GET\"\n");
+}
+
+static void cmd_get_app(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_MODEL_APP_GET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 2) {
+               bt_shell_printf("Bad arguments: %s\n", argv[1]);
+               return;
+       }
+
+       /* Per Mesh Profile 4.3.2.49 */
+       /* Element Address */
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Model ID */
+       put_le16(parms[1], msg + n);
+       n += 2;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"GET APP GET\"\n");
+}
+
+static void cmd_set_hb(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       int parm_cnt;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_CONFIG_HEARTBEAT_PUB_SET, msg);
+
+       parm_cnt = read_input_parameters(argc, argv);
+       if (parm_cnt != 5) {
+               bt_shell_printf("Bad arguments: %s\n", argv[1]);
+               return;
+       }
+
+       /* Per Mesh Profile 4.3.2.62 */
+       /* Publish address */
+       put_le16(parms[0], msg + n);
+       n += 2;
+       /* Count Log */
+       msg[n++] = parms[1];
+       /* Period Log */
+       msg[n++] = parms[2];
+       /* Heartbeat TTL */
+       msg[n++] = DEFAULT_TTL;
+       /* Features */
+       put_le16(parms[3], msg + n);
+       n += 2;
+       /* NetKey Index */
+       put_le16(parms[4], msg + n);
+       n += 2;
+
+       if (!config_send(msg, n))
+               bt_shell_printf("Failed to send \"SET HEARTBEAT PUBLICATION\"\n");
+}
+
+static void cmd_get_ttl(int argc, char *argv[])
+{
+       cmd_default(OP_CONFIG_DEFAULT_TTL_GET);
+}
+
+static const struct bt_shell_menu cfg_menu = {
+       .name = "config",
+       .desc = "Configuration Model Submenu",
+       .entries = {
+       {"target",              "<unicast>",            cmd_set_node,
+                                               "Set target node to configure"},
+       {"composition-get",     "[page_num]",           cmd_get_composition,
+                                               "Get Composition Data"},
+       {"netkey-add",          "<net_idx>",            cmd_add_net_key,
+                                               "Add network key"},
+       {"netkey-del",          "<net_idx>",            cmd_del_net_key,
+                                               "Delete network key"},
+       {"appkey-add",          "<app_idx>",            cmd_add_app_key,
+                                               "Add application key"},
+       {"appkey-del",          "<app_idx>",            cmd_del_app_key,
+                                               "Delete application key"},
+       {"bind",                "<ele_idx> <app_idx> <mod_id> [cid]",
+                               cmd_bind,       "Bind app key to a model"},
+       {"ttl-set",             "<ttl>",                cmd_set_ttl,
+                                               "Set default TTL"},
+       {"ttl-get",             NULL,                   cmd_get_ttl,
+                                               "Get default TTL"},
+       {"pub-set", "<ele_addr> <pub_addr> <app_idx> "
+                       "<per (step|res)> <re-xmt (cnt|per)> <mod id> "
+                       "[cid]",
+                       cmd_set_pub,    "\n\t\t\t\t\t\t  Set publication"},
+       {"pub-get", "<ele_addr> <model>",               cmd_get_pub,
+                                               "Get publication"},
+       {"proxy-set",           "<proxy>",              cmd_set_proxy,
+                                               "Set proxy state"},
+       {"proxy-get",           NULL,                   cmd_get_proxy,
+                                               "Get proxy state"},
+       {"ident-set",           "<net_idx> <state>",    cmd_set_ident,
+                                               "Set node identity state"},
+       {"ident-get",           "<net_idx>",            cmd_get_ident,
+                                               "Get node identity state"},
+       {"relay-set",           "<relay> <rexmt count> <rexmt steps>",
+                                               cmd_set_relay,
+                                               "Set relay"},
+       {"relay-get",           NULL,                   cmd_get_relay,
+                                               "Get relay"},
+       {"hb-pub-set", "<pub_addr> <count> <period> <features> <net_idx>",
+                               cmd_set_hb,     "Set heartbeati publish"},
+       {"sub-add", "<ele_addr> <sub_addr> <model id>",
+                               cmd_sub_add,    "Subscription add"},
+       {"sub-get", "<ele_addr> <model id>",
+                               cmd_sub_get,    "Subscription get"},
+       {"app-get", "<ele_addr> <model id>",
+                               cmd_get_app,    "Get App Keys"},
+       {} },
+};
+
+void config_client_get_composition(uint32_t dst)
+{
+       uint32_t tmp = target;
+
+       target = dst;
+       cmd_get_composition(0, NULL);
+       target = tmp;
+}
+
+static struct mesh_model_ops client_cbs = {
+       client_msg_recvd,
+               NULL,
+               NULL,
+               NULL
+};
+
+bool config_client_init(void)
+{
+       if (!node_local_model_register(PRIMARY_ELEMENT_IDX,
+                                               CONFIG_CLIENT_MODEL_ID,
+                                               &client_cbs, NULL))
+               return false;
+
+       bt_shell_add_submenu(&cfg_menu);
+
+       return true;
+}
diff --git a/mesh/config-model.h b/mesh/config-model.h
new file mode 100644 (file)
index 0000000..a5b8113
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#define CONFIG_SERVER_MODEL_ID 0x0000
+#define CONFIG_CLIENT_MODEL_ID 0x0001
+
+#define OP_APPKEY_ADD                          0x00
+#define OP_APPKEY_DELETE                       0x8000
+#define OP_APPKEY_GET                          0x8001
+#define OP_APPKEY_LIST                         0x8002
+#define OP_APPKEY_STATUS                       0x8003
+#define OP_APPKEY_UPDATE                       0x01
+#define OP_DEV_COMP_GET                                0x8008
+#define OP_DEV_COMP_STATUS                     0x02
+#define OP_CONFIG_BEACON_GET                   0x8009
+#define OP_CONFIG_BEACON_SET                   0x800A
+#define OP_CONFIG_BEACON_STATUS                        0x800B
+#define OP_CONFIG_DEFAULT_TTL_GET              0x800C
+#define OP_CONFIG_DEFAULT_TTL_SET              0x800D
+#define OP_CONFIG_DEFAULT_TTL_STATUS           0x800E
+#define OP_CONFIG_FRIEND_GET                   0x800F
+#define OP_CONFIG_FRIEND_SET                   0x8010
+#define OP_CONFIG_FRIEND_STATUS                        0x8011
+#define OP_CONFIG_PROXY_GET                    0x8012
+#define OP_CONFIG_PROXY_SET                    0x8013
+#define OP_CONFIG_PROXY_STATUS                 0x8014
+#define OP_CONFIG_KEY_REFRESH_PHASE_GET                0x8015
+#define OP_CONFIG_KEY_REFRESH_PHASE_SET                0x8016
+#define OP_CONFIG_KEY_REFRESH_PHASE_STATUS     0x8017
+#define OP_CONFIG_MODEL_PUB_GET                        0x8018
+#define OP_CONFIG_MODEL_PUB_SET                        0x03
+#define OP_CONFIG_MODEL_PUB_STATUS             0x8019
+#define OP_CONFIG_MODEL_PUB_VIRT_SET           0x801A
+#define OP_CONFIG_MODEL_SUB_ADD                        0x801B
+#define OP_CONFIG_MODEL_SUB_DELETE             0x801C
+#define OP_CONFIG_MODEL_SUB_DELETE_ALL         0x801D
+#define OP_CONFIG_MODEL_SUB_OVERWRITE          0x801E
+#define OP_CONFIG_MODEL_SUB_STATUS             0x801F
+#define OP_CONFIG_MODEL_SUB_VIRT_ADD           0x8020
+#define OP_CONFIG_MODEL_SUB_VIRT_DELETE                0x8021
+#define OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE     0x8022
+#define OP_CONFIG_NETWORK_TRANSMIT_GET         0x8023
+#define OP_CONFIG_NETWORK_TRANSMIT_SET         0x8024
+#define OP_CONFIG_NETWORK_TRANSMIT_STATUS      0x8025
+#define OP_CONFIG_RELAY_GET                    0x8026
+#define OP_CONFIG_RELAY_SET                    0x8027
+#define OP_CONFIG_RELAY_STATUS                 0x8028
+#define OP_CONFIG_MODEL_SUB_GET                        0x8029
+#define OP_CONFIG_MODEL_SUB_LIST               0x802A
+#define OP_CONFIG_VEND_MODEL_SUB_GET           0x802B
+#define OP_CONFIG_VEND_MODEL_SUB_LIST          0x802C
+#define OP_CONFIG_POLL_TIMEOUT_LIST            0x802D
+#define OP_CONFIG_POLL_TIMEOUT_STATUS          0x802E
+#define OP_CONFIG_HEARTBEAT_PUB_GET            0x8038
+#define OP_CONFIG_HEARTBEAT_PUB_SET            0x8039
+#define OP_CONFIG_HEARTBEAT_PUB_STATUS         0x06
+#define OP_CONFIG_HEARTBEAT_SUB_GET            0x803A
+#define OP_CONFIG_HEARTBEAT_SUB_SET            0x803B
+#define OP_CONFIG_HEARTBEAT_SUB_STATUS         0x803C
+#define OP_MODEL_APP_BIND                      0x803D
+#define OP_MODEL_APP_STATUS                    0x803E
+#define OP_MODEL_APP_UNBIND                    0x803F
+#define OP_NETKEY_ADD                          0x8040
+#define OP_NETKEY_DELETE                       0x8041
+#define OP_NETKEY_GET                          0x8042
+#define OP_NETKEY_LIST                         0x8043
+#define OP_NETKEY_STATUS                       0x8044
+#define OP_NETKEY_UPDATE                       0x8045
+#define OP_NODE_IDENTITY_GET                   0x8046
+#define OP_NODE_IDENTITY_SET                   0x8047
+#define OP_NODE_IDENTITY_STATUS                        0x8048
+#define OP_NODE_RESET                          0x8049
+#define OP_NODE_RESET_STATUS                   0x804A
+#define OP_MODEL_APP_GET                       0x804B
+#define OP_MODEL_APP_LIST                      0x804C
+#define OP_VEND_MODEL_APP_GET                  0x804D
+#define OP_VEND_MODEL_APP_LIST                 0x804E
+
+bool config_server_init(void);
+bool config_client_init(void);
+void config_client_get_composition(uint32_t dst);
diff --git a/mesh/config-server.c b/mesh/config-server.c
new file mode 100644 (file)
index 0000000..10fead6
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/config-model.h"
+
+static bool server_msg_recvd(uint16_t src, uint8_t *data,
+                               uint16_t len, void *user_data)
+{
+       uint32_t opcode;
+       uint8_t msg[32];
+       struct mesh_node *node;
+       uint16_t primary;
+       uint32_t mod_id;
+       uint16_t ele_addr;
+       uint8_t ele_idx;
+       struct mesh_publication pub;
+       int m, n;
+
+       if (mesh_opcode_get(data, len, &opcode, &n)) {
+               len -= n;
+               data += n;
+       } else
+               return false;
+
+       node = node_get_local_node();
+
+       if (!node)
+               return true;
+
+       n = 0;
+
+       switch (opcode & ~OP_UNRELIABLE) {
+       default:
+               return false;
+
+       case OP_CONFIG_DEFAULT_TTL_SET:
+               if (len != 1 || data[0] > TTL_MASK || data[0] == 1)
+                       return true;
+
+               if (data[0] <= TTL_MASK) {
+                       node_set_default_ttl(node, data[0]);
+                       prov_db_node_set_ttl(node, data[0]);
+               }
+
+               /* Fall Through */
+
+       case OP_CONFIG_DEFAULT_TTL_GET:
+               n = mesh_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg);
+               msg[n++] = node_get_default_ttl(node);
+               break;
+
+       case OP_CONFIG_MODEL_PUB_SET:
+
+               if (len != 11 && len != 13)
+                       return true;
+
+               bt_shell_printf("Set publication\n");
+
+               ele_addr = get_le16(data);
+               mod_id = get_le16(data + 9);
+               if (len == 14)
+                       mod_id = (mod_id << 16)  | get_le16(data + 11);
+               else
+                       mod_id |= 0xffff0000;
+
+               pub.u.addr16 = get_le16(data + 2);
+               pub.app_idx = get_le16(data + 4);
+               pub.ttl = data[6];
+               pub.period = data[7];
+               m = (data[7] & 0x3f);
+               switch (data[7] >> 6) {
+               case 0:
+                       bt_shell_printf("Period: %d ms\n", m * 100);
+                       break;
+               case 2:
+                       m *= 10;
+                       /* fall through */
+               case 1:
+                       bt_shell_printf("Period: %d sec\n", m);
+                       break;
+               case 3:
+                       bt_shell_printf("Period: %d min\n", m * 10);
+                       break;
+               }
+
+               pub.retransmit = data[8];
+               bt_shell_printf("Retransmit count: %d\n", data[8] >> 5);
+               bt_shell_printf("Retransmit Interval Steps: %d\n", data[8] & 0x1f);
+
+               ele_idx = ele_addr - node_get_primary(node);
+
+               if (node_model_pub_set(node, ele_idx, mod_id, &pub)) {
+                       prov_db_node_set_model_pub(node, ele_idx, mod_id,
+                                    node_model_pub_get(node, ele_idx, mod_id));
+               }
+               break;
+       }
+
+       if (!n)
+               return true;
+
+       primary = node_get_primary(node);
+       if (src != primary)
+               net_access_layer_send(node_get_default_ttl(node), primary,
+                                       src, APP_IDX_DEV, msg, n);
+       else
+               node_local_data_handler(primary, src, node_get_iv_index(node),
+                                       node_get_sequence_number(node),
+                                       APP_IDX_DEV, msg, n);
+       return true;
+}
+
+
+static struct mesh_model_ops server_cbs = {
+       server_msg_recvd,
+               NULL,
+               NULL,
+               NULL
+};
+
+bool config_server_init(void)
+{
+       if (!node_local_model_register(PRIMARY_ELEMENT_IDX,
+                                               CONFIG_SERVER_MODEL_ID,
+                                               &server_cbs, NULL))
+               return false;
+
+       return true;
+}
diff --git a/mesh/crypto.c b/mesh/crypto.c
new file mode 100644 (file)
index 0000000..efb9df8
--- /dev/null
@@ -0,0 +1,1168 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <linux/if_alg.h>
+
+#include <glib.h>
+
+#ifndef SOL_ALG
+#define SOL_ALG                279
+#endif
+
+#ifndef ALG_SET_AEAD_AUTHSIZE
+#define ALG_SET_AEAD_AUTHSIZE  5
+#endif
+
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/crypto.h"
+
+static int alg_new(int fd, const void *keyval, socklen_t keylen,
+               size_t mic_size)
+{
+       if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0) {
+               g_printerr("key");
+               return -1;
+       }
+
+       if (mic_size &&
+               setsockopt(fd, SOL_ALG,
+                       ALG_SET_AEAD_AUTHSIZE, NULL, mic_size) < 0) {
+               g_printerr("taglen");
+               return -1;
+       }
+
+       /* FIXME: This should use accept4() with SOCK_CLOEXEC */
+       return accept(fd, NULL, 0);
+}
+
+static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
+                                               void *outbuf, size_t outlen)
+{
+       __u32 alg_op = ALG_OP_ENCRYPT;
+       char cbuf[CMSG_SPACE(sizeof(alg_op))];
+       struct cmsghdr *cmsg;
+       struct msghdr msg;
+       struct iovec iov;
+       ssize_t len;
+
+       memset(cbuf, 0, sizeof(cbuf));
+       memset(&msg, 0, sizeof(msg));
+
+       msg.msg_control = cbuf;
+       msg.msg_controllen = sizeof(cbuf);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_OP;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
+       memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
+
+       iov.iov_base = (void *) inbuf;
+       iov.iov_len = inlen;
+
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       len = sendmsg(fd, &msg, 0);
+       if (len < 0)
+               return false;
+
+       len = read(fd, outbuf, outlen);
+       if (len < 0)
+               return false;
+
+       return true;
+}
+
+static int aes_ecb_setup(const uint8_t key[16])
+{
+       struct sockaddr_alg salg;
+       int fd, nfd;
+
+       fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -1;
+
+       memset(&salg, 0, sizeof(salg));
+       salg.salg_family = AF_ALG;
+       strcpy((char *) salg.salg_type, "skcipher");
+       strcpy((char *) salg.salg_name, "ecb(aes)");
+
+       if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       nfd = alg_new(fd, key, 16, 0);
+
+       close(fd);
+
+       return nfd;
+}
+
+static bool aes_ecb(int fd, const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+       return alg_encrypt(fd, plaintext, 16, encrypted, 16);
+}
+
+static void aes_ecb_destroy(int fd)
+{
+       close(fd);
+}
+
+static bool aes_ecb_one(const uint8_t key[16],
+                       const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+       bool result;
+       int fd;
+
+       fd = aes_ecb_setup(key);
+       if (fd < 0)
+               return false;
+
+       result = aes_ecb(fd, plaintext, encrypted);
+
+       aes_ecb_destroy(fd);
+
+       return result;
+}
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX   (64 + 64 + 17)
+
+static int aes_cmac_setup(const uint8_t key[16])
+{
+       struct sockaddr_alg salg;
+       int fd, nfd;
+
+       fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -1;
+
+       memset(&salg, 0, sizeof(salg));
+       salg.salg_family = AF_ALG;
+       strcpy((char *) salg.salg_type, "hash");
+       strcpy((char *) salg.salg_name, "cmac(aes)");
+
+       if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       nfd = alg_new(fd, key, 16, 0);
+
+       close(fd);
+
+       return nfd;
+}
+
+static bool aes_cmac(int fd, const uint8_t *msg,
+                                       size_t msg_len, uint8_t res[16])
+{
+       ssize_t len;
+
+       if (msg_len > CMAC_MSG_MAX)
+               return false;
+
+       len = send(fd, msg, msg_len, 0);
+       if (len < 0)
+               return false;
+
+       len = read(fd, res, 16);
+       if (len < 0)
+               return false;
+
+       return true;
+}
+
+static void aes_cmac_destroy(int fd)
+{
+       close(fd);
+}
+
+static int aes_cmac_N_start(const uint8_t N[16])
+{
+       int fd;
+
+       fd = aes_cmac_setup(N);
+       return fd;
+}
+
+static bool aes_cmac_one(const uint8_t key[16], const void *msg,
+                                       size_t msg_len, uint8_t res[16])
+{
+       bool result;
+       int fd;
+
+       fd = aes_cmac_setup(key);
+       if (fd < 0)
+               return false;
+
+       result = aes_cmac(fd, msg, msg_len, res);
+
+       aes_cmac_destroy(fd);
+
+       return result;
+}
+
+bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
+                                       size_t msg_len, uint8_t res[16])
+{
+       return aes_cmac_one(key, msg, msg_len, res);
+}
+
+bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
+                                       const uint8_t *aad, uint16_t aad_len,
+                                       const uint8_t *msg, uint16_t msg_len,
+                                       uint8_t *out_msg, void *out_mic,
+                                       size_t mic_size)
+{
+       uint8_t pmsg[16], cmic[16], cmsg[16];
+       uint8_t mic[16], Xn[16];
+       uint16_t blk_cnt, last_blk;
+       bool result;
+       size_t i, j;
+       int fd;
+
+       if (aad_len >= 0xff00) {
+               g_printerr("Unsupported AAD size");
+               return false;
+       }
+
+       fd = aes_ecb_setup(key);
+       if (fd < 0)
+               return false;
+
+       /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+       pmsg[0] = 0x01;
+       memcpy(pmsg + 1, nonce, 13);
+       put_be16(0x0000, pmsg + 14);
+
+       result = aes_ecb(fd, pmsg, cmic);
+       if (!result)
+               goto done;
+
+       /* X_0 = e(AppKey, 0x09 || nonce || length) */
+       if (mic_size == sizeof(uint64_t))
+               pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+       else
+               pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+
+       memcpy(pmsg + 1, nonce, 13);
+       put_be16(msg_len, pmsg + 14);
+
+       result = aes_ecb(fd, pmsg, Xn);
+       if (!result)
+               goto done;
+
+       /* If AAD is being used to authenticate, include it here */
+       if (aad_len) {
+               put_be16(aad_len, pmsg);
+
+               for (i = 0; i < sizeof(uint16_t); i++)
+                       pmsg[i] = Xn[i] ^ pmsg[i];
+
+               j = 0;
+               aad_len += sizeof(uint16_t);
+               while (aad_len > 16) {
+                       do {
+                               pmsg[i] = Xn[i] ^ aad[j];
+                               i++, j++;
+                       } while (i < 16);
+
+                       aad_len -= 16;
+                       i = 0;
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+               }
+
+               for (i = 0; i < aad_len; i++, j++)
+                       pmsg[i] = Xn[i] ^ aad[j];
+
+               for (i = aad_len; i < 16; i++)
+                       pmsg[i] = Xn[i];
+
+               result = aes_ecb(fd, pmsg, Xn);
+               if (!result)
+                       goto done;
+       }
+
+       last_blk = msg_len % 16;
+       blk_cnt = (msg_len + 15) / 16;
+       if (!last_blk)
+               last_blk = 16;
+
+       for (j = 0; j < blk_cnt; j++) {
+               if (j + 1 == blk_cnt) {
+                       /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+                       for (i = 0; i < last_blk; i++)
+                               pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+                       for (i = last_blk; i < 16; i++)
+                               pmsg[i] = Xn[i] ^ 0x00;
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+
+                       /* MIC = C_mic ^ X_1 */
+                       for (i = 0; i < sizeof(mic); i++)
+                               mic[i] = cmic[i] ^ Xn[i];
+
+                       /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+                       pmsg[0] = 0x01;
+                       memcpy(pmsg + 1, nonce, 13);
+                       put_be16(j + 1, pmsg + 14);
+
+                       result = aes_ecb(fd, pmsg, cmsg);
+                       if (!result)
+                               goto done;
+
+                       if (out_msg) {
+                               /* Encrypted = Payload[0-15] ^ C_1 */
+                               for (i = 0; i < last_blk; i++)
+                                       out_msg[(j * 16) + i] =
+                                               msg[(j * 16) + i] ^ cmsg[i];
+
+                       }
+               } else {
+                       /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+                       for (i = 0; i < 16; i++)
+                               pmsg[i] = Xn[i] ^ msg[(j * 16) + i];
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+
+                       /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+                       pmsg[0] = 0x01;
+                       memcpy(pmsg + 1, nonce, 13);
+                       put_be16(j + 1, pmsg + 14);
+
+                       result = aes_ecb(fd, pmsg, cmsg);
+                       if (!result)
+                               goto done;
+
+                       if (out_msg) {
+                               /* Encrypted = Payload[0-15] ^ C_N */
+                               for (i = 0; i < 16; i++)
+                                       out_msg[(j * 16) + i] =
+                                               msg[(j * 16) + i] ^ cmsg[i];
+                       }
+
+               }
+       }
+
+       if (out_msg)
+               memcpy(out_msg + msg_len, mic, mic_size);
+
+       if (out_mic) {
+               switch (mic_size) {
+               case sizeof(uint32_t):
+                       *(uint32_t *)out_mic = get_be32(mic);
+                       break;
+               case sizeof(uint64_t):
+                       *(uint64_t *)out_mic = get_be64(mic);
+                       break;
+               default:
+                       g_printerr("Unsupported MIC size");
+               }
+       }
+
+done:
+       aes_ecb_destroy(fd);
+
+       return result;
+}
+
+bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
+                               const uint8_t *aad, uint16_t aad_len,
+                               const uint8_t *enc_msg, uint16_t enc_msg_len,
+                               uint8_t *out_msg, void *out_mic,
+                               size_t mic_size)
+{
+       uint8_t msg[16], pmsg[16], cmic[16], cmsg[16], Xn[16];
+       uint8_t mic[16];
+       uint16_t msg_len = enc_msg_len - mic_size;
+       uint16_t last_blk, blk_cnt;
+       bool result;
+       size_t i, j;
+       int fd;
+
+       if (enc_msg_len < 5 || aad_len >= 0xff00)
+               return false;
+
+       fd = aes_ecb_setup(key);
+       if (fd < 0)
+               return false;
+
+       /* C_mic = e(AppKey, 0x01 || nonce || 0x0000) */
+       pmsg[0] = 0x01;
+       memcpy(pmsg + 1, nonce, 13);
+       put_be16(0x0000, pmsg + 14);
+
+       result = aes_ecb(fd, pmsg, cmic);
+       if (!result)
+               goto done;
+
+       /* X_0 = e(AppKey, 0x09 || nonce || length) */
+       if (mic_size == sizeof(uint64_t))
+               pmsg[0] = 0x19 | (aad_len ? 0x40 : 0x00);
+       else
+               pmsg[0] = 0x09 | (aad_len ? 0x40 : 0x00);
+
+       memcpy(pmsg + 1, nonce, 13);
+       put_be16(msg_len, pmsg + 14);
+
+       result = aes_ecb(fd, pmsg, Xn);
+       if (!result)
+               goto done;
+
+       /* If AAD is being used to authenticate, include it here */
+       if (aad_len) {
+               put_be16(aad_len, pmsg);
+
+               for (i = 0; i < sizeof(uint16_t); i++)
+                       pmsg[i] = Xn[i] ^ pmsg[i];
+
+               j = 0;
+               aad_len += sizeof(uint16_t);
+               while (aad_len > 16) {
+                       do {
+                               pmsg[i] = Xn[i] ^ aad[j];
+                               i++, j++;
+                       } while (i < 16);
+
+                       aad_len -= 16;
+                       i = 0;
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+               }
+
+               for (i = 0; i < aad_len; i++, j++)
+                       pmsg[i] = Xn[i] ^ aad[j];
+
+               for (i = aad_len; i < 16; i++)
+                       pmsg[i] = Xn[i];
+
+               result = aes_ecb(fd, pmsg, Xn);
+               if (!result)
+                       goto done;
+       }
+
+       last_blk = msg_len % 16;
+       blk_cnt = (msg_len + 15) / 16;
+       if (!last_blk)
+               last_blk = 16;
+
+       for (j = 0; j < blk_cnt; j++) {
+               if (j + 1 == blk_cnt) {
+                       /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+                       pmsg[0] = 0x01;
+                       memcpy(pmsg + 1, nonce, 13);
+                       put_be16(j + 1, pmsg + 14);
+
+                       result = aes_ecb(fd, pmsg, cmsg);
+                       if (!result)
+                               goto done;
+
+                       /* Encrypted = Payload[0-15] ^ C_1 */
+                       for (i = 0; i < last_blk; i++)
+                               msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+
+                       if (out_msg)
+                               memcpy(out_msg + (j * 16), msg, last_blk);
+
+                       /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+                       for (i = 0; i < last_blk; i++)
+                               pmsg[i] = Xn[i] ^ msg[i];
+                       for (i = last_blk; i < 16; i++)
+                               pmsg[i] = Xn[i] ^ 0x00;
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+
+                       /* MIC = C_mic ^ X_1 */
+                       for (i = 0; i < sizeof(mic); i++)
+                               mic[i] = cmic[i] ^ Xn[i];
+               } else {
+                       /* C_1 = e(AppKey, 0x01 || nonce || 0x0001) */
+                       pmsg[0] = 0x01;
+                       memcpy(pmsg + 1, nonce, 13);
+                       put_be16(j + 1, pmsg + 14);
+
+                       result = aes_ecb(fd, pmsg, cmsg);
+                       if (!result)
+                               goto done;
+
+                       /* Encrypted = Payload[0-15] ^ C_1 */
+                       for (i = 0; i < 16; i++)
+                               msg[i] = enc_msg[(j * 16) + i] ^ cmsg[i];
+
+                       if (out_msg)
+                               memcpy(out_msg + (j * 16), msg, 16);
+
+                       /* X_1 = e(AppKey, X_0 ^ Payload[0-15]) */
+                       for (i = 0; i < 16; i++)
+                               pmsg[i] = Xn[i] ^ msg[i];
+
+                       result = aes_ecb(fd, pmsg, Xn);
+                       if (!result)
+                               goto done;
+               }
+       }
+
+       switch (mic_size) {
+               case sizeof(uint32_t):
+                       if (out_mic)
+                               *(uint32_t *)out_mic = get_be32(mic);
+                       else if (get_be32(enc_msg + enc_msg_len - mic_size) !=
+                                       get_be32(mic))
+                               result = false;
+                       break;
+
+               case sizeof(uint64_t):
+                       if (out_mic)
+                               *(uint64_t *)out_mic = get_be64(mic);
+                       else if (get_be64(enc_msg + enc_msg_len - mic_size) !=
+                                       get_be64(mic))
+                               result = false;
+                       break;
+
+               default:
+                       g_printerr("Unsupported MIC size");
+                       result = false;
+       }
+
+done:
+       aes_ecb_destroy(fd);
+
+       return result;
+}
+
+bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
+               const void *info, size_t info_len, uint8_t okm[16])
+{
+       uint8_t res[16];
+
+       if (!aes_cmac_one(salt, ikm, 16, res))
+               return false;
+
+       return aes_cmac_one(res, info, info_len, okm);
+}
+
+bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
+                                                       uint8_t net_id[1],
+                                                       uint8_t enc_key[16],
+                                                       uint8_t priv_key[16])
+{
+       int fd;
+       uint8_t output[16];
+       uint8_t t[16];
+       uint8_t *stage;
+       bool success = false;
+
+       stage = g_malloc(sizeof(output) + p_len + 1);
+       if (stage == NULL)
+               return false;
+
+       if (!mesh_crypto_s1("smk2", 4, stage))
+               goto fail;
+
+       if (!aes_cmac_one(stage, n, 16, t))
+               goto fail;
+
+       fd = aes_cmac_N_start(t);
+       if (fd < 0)
+               goto fail;
+
+       memcpy(stage, p, p_len);
+       stage[p_len] = 1;
+
+       if(!aes_cmac(fd, stage, p_len + 1, output))
+               goto done;
+
+       net_id[0] = output[15] & 0x7f;
+
+       memcpy(stage, output, 16);
+       memcpy(stage + 16, p, p_len);
+       stage[p_len + 16] = 2;
+
+       if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
+               goto done;
+
+       memcpy(enc_key, output, 16);
+
+       memcpy(stage, output, 16);
+       memcpy(stage + 16, p, p_len);
+       stage[p_len + 16] = 3;
+
+       if(!aes_cmac(fd, stage, p_len + 16 + 1, output))
+               goto done;
+
+       memcpy(priv_key, output, 16);
+       success = true;
+
+done:
+       aes_cmac_destroy(fd);
+fail:
+       g_free(stage);
+
+       return success;
+}
+
+static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16])
+{
+       uint8_t id128[] = { 'i', 'd', '1', '2', '8', 0x01 };
+       uint8_t salt[16];
+
+       if (!mesh_crypto_s1(s, 4, salt))
+               return false;
+
+       return mesh_crypto_k1(n, salt, id128, sizeof(id128), out128);
+}
+
+bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16])
+{
+       return crypto_128(n, "nkik", identity_key);
+}
+
+static bool identity_calc(const uint8_t net_key[16], uint16_t addr,
+               bool check, uint8_t id[16])
+{
+       uint8_t id_key[16];
+       uint8_t tmp[16];
+
+       if (!mesh_crypto_nkik(net_key, id_key))
+               return false;
+
+       memset(tmp, 0, sizeof(tmp));
+       put_be16(addr, tmp + 14);
+
+       if (check) {
+               memcpy(tmp + 6, id + 8, 8);
+       } else {
+               mesh_get_random_bytes(tmp + 6, 8);
+               memcpy(id + 8, tmp + 6, 8);
+       }
+
+       if (!aes_ecb_one(id_key, tmp, tmp))
+               return false;
+
+       if (check)
+               return (memcmp(id, tmp + 8, 8) == 0);
+
+       memcpy(id, tmp + 8, 8);
+       return true;
+}
+
+bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
+                                                       uint8_t id[16])
+{
+       return identity_calc(net_key, addr, false, id);
+}
+
+bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,
+                                                       uint8_t id[16])
+{
+       return identity_calc(net_key, addr, true, id);
+}
+
+bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])
+{
+       return crypto_128(n, "nkbk", beacon_key);
+}
+
+bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])
+{
+       uint8_t tmp[16];
+       uint8_t t[16];
+       uint8_t id64[] = { 'i', 'd', '6', '4', 0x01 };
+
+       if (!mesh_crypto_s1("smk3", 4, tmp))
+               return false;
+
+       if (!aes_cmac_one(tmp, n, 16, t))
+               return false;
+
+       if (!aes_cmac_one(t, id64, sizeof(id64), tmp))
+               return false;
+
+       memcpy(out64, tmp + 8, 8);
+
+       return true;
+}
+
+bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1])
+{
+       uint8_t tmp[16];
+       uint8_t t[16];
+       uint8_t id6[] = { 'i', 'd', '6', 0x01 };
+
+       if (!mesh_crypto_s1("smk4", 4, tmp))
+               return false;
+
+       if (!aes_cmac_one(tmp, a, 16, t))
+               return false;
+
+       if (!aes_cmac_one(t, id6, sizeof(id6), tmp))
+               return false;
+
+       out6[0] = tmp[15] & 0x3f;
+       return true;
+}
+
+bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
+                               const uint8_t network_id[8],
+                               uint32_t iv_index, bool kr, bool iu,
+                               uint64_t *cmac)
+{
+       uint8_t msg[13], tmp[16];
+
+       if (!cmac)
+               return false;
+
+       msg[0] = kr ? 0x01 : 0x00;
+       msg[0] |= iu ? 0x02 : 0x00;
+       memcpy(msg + 1, network_id, 8);
+       put_be32(iv_index, msg + 9);
+
+       if (!aes_cmac_one(encryption_key, msg, 13, tmp))
+               return false;
+
+       *cmac = get_be64(tmp);
+
+       return true;
+}
+
+bool mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq,
+                               uint16_t src, uint32_t iv_index,
+                               uint8_t nonce[13])
+{
+       nonce[0] = 0;
+       nonce[1] = (ttl & TTL_MASK) | (ctl ? CTL : 0x00);
+       nonce[2] = (seq >> 16) & 0xff;
+       nonce[3] = (seq >> 8) & 0xff;
+       nonce[4] = seq & 0xff;
+
+       /* SRC */
+       put_be16(src, nonce + 5);
+
+       put_be16(0, nonce + 7);
+
+       /* IV Index */
+       put_be32(iv_index, nonce + 9);
+
+       return true;
+}
+
+bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
+                               uint32_t seq, uint16_t src,
+                               uint32_t iv_index,
+                               const uint8_t net_key[16],
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *net_mic)
+{
+       uint8_t nonce[13];
+
+       if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
+               return false;
+
+       return mesh_crypto_aes_ccm_encrypt(nonce, net_key,
+                               NULL, 0, enc_msg,
+                               enc_msg_len, out,
+                               net_mic,
+                               ctl ? sizeof(uint64_t) : sizeof(uint32_t));
+}
+
+bool mesh_crypto_network_decrypt(bool ctl, uint8_t ttl,
+                               uint32_t seq, uint16_t src,
+                               uint32_t iv_index,
+                               const uint8_t net_key[16],
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *net_mic, size_t mic_size)
+{
+       uint8_t nonce[13];
+
+       if (!mesh_crypto_network_nonce(ctl, ttl, seq, src, iv_index, nonce))
+               return false;
+
+       return mesh_crypto_aes_ccm_decrypt(nonce, net_key, NULL, 0,
+                                               enc_msg, enc_msg_len, out,
+                                               net_mic, mic_size);
+}
+
+bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
+                                       uint16_t dst, uint32_t iv_index,
+                                       bool aszmic, uint8_t nonce[13])
+{
+       nonce[0] = 0x01;
+       nonce[1] = aszmic ? 0x80 : 0x00;
+       nonce[2] = (seq & 0x00ff0000) >> 16;
+       nonce[3] = (seq & 0x0000ff00) >> 8;
+       nonce[4] = (seq & 0x000000ff);
+       nonce[5] = (src & 0xff00) >> 8;
+       nonce[6] = (src & 0x00ff);
+       nonce[7] = (dst & 0xff00) >> 8;
+       nonce[8] = (dst & 0x00ff);
+       put_be32(iv_index, nonce + 9);
+
+       return true;
+}
+
+bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
+                                       uint16_t dst, uint32_t iv_index,
+                                       bool aszmic, uint8_t nonce[13])
+{
+       nonce[0] = 0x02;
+       nonce[1] = aszmic ? 0x80 : 0x00;
+       nonce[2] = (seq & 0x00ff0000) >> 16;
+       nonce[3] = (seq & 0x0000ff00) >> 8;
+       nonce[4] = (seq & 0x000000ff);
+       nonce[5] = (src & 0xff00) >> 8;
+       nonce[6] = (src & 0x00ff);
+       nonce[7] = (dst & 0xff00) >> 8;
+       nonce[8] = (dst & 0x00ff);
+       put_be32(iv_index, nonce + 9);
+
+       return true;
+}
+
+bool mesh_crypto_application_encrypt(uint8_t key_id, uint32_t seq, uint16_t src,
+                                       uint16_t dst, uint32_t iv_index,
+                                       const uint8_t app_key[16],
+                                       const uint8_t *aad, uint8_t aad_len,
+                                       const uint8_t *msg, uint8_t msg_len,
+                                       uint8_t *out, void *app_mic,
+                                       size_t mic_size)
+{
+       uint8_t nonce[13];
+       bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
+
+       if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
+                               iv_index, aszmic, nonce))
+               return false;
+
+       if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
+                               iv_index, aszmic, nonce))
+               return false;
+
+       return mesh_crypto_aes_ccm_encrypt(nonce, app_key, aad, aad_len,
+                                               msg, msg_len,
+                                               out, app_mic, mic_size);
+}
+
+bool mesh_crypto_application_decrypt(uint8_t key_id, uint32_t seq, uint16_t src,
+                               uint16_t dst, uint32_t iv_index,
+                               const uint8_t app_key[16],
+                               const uint8_t *aad, uint8_t aad_len,
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *app_mic, size_t mic_size)
+{
+       uint8_t nonce[13];
+       bool aszmic = (mic_size == sizeof(uint64_t)) ? true : false;
+
+       if (!key_id && !mesh_crypto_device_nonce(seq, src, dst,
+                               iv_index, aszmic, nonce))
+               return false;
+
+       if (key_id && !mesh_crypto_application_nonce(seq, src, dst,
+                               iv_index, aszmic, nonce))
+               return false;
+
+       return mesh_crypto_aes_ccm_decrypt(nonce, app_key,
+                                               aad, aad_len, enc_msg,
+                                               enc_msg_len, out,
+                                               app_mic, mic_size);
+}
+
+bool mesh_crypto_session_key(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t session_key[16])
+{
+       const uint8_t prsk[4] = "prsk";
+
+       if (!aes_cmac_one(salt, secret, 32, session_key))
+               return false;
+
+       return aes_cmac_one(session_key, prsk, 4, session_key);
+}
+
+bool mesh_crypto_nonce(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t nonce[13])
+{
+       const uint8_t prsn[4] = "prsn";
+       uint8_t tmp[16];
+       bool result;
+
+       if (!aes_cmac_one(salt, secret, 32, tmp))
+               return false;
+
+       result =  aes_cmac_one(tmp, prsn, 4, tmp);
+
+       if (result)
+               memcpy(nonce, tmp + 3, 13);
+
+       return result;
+}
+
+bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16])
+{
+       const uint8_t zero[16] = {0};
+
+       return aes_cmac_one(zero, info, len, salt);
+}
+
+bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
+                                       const uint8_t prov_rand[16],
+                                       const uint8_t dev_rand[16],
+                                       uint8_t prov_salt[16])
+{
+       const uint8_t zero[16] = {0};
+       uint8_t tmp[16 * 3];
+
+       memcpy(tmp, conf_salt, 16);
+       memcpy(tmp + 16, prov_rand, 16);
+       memcpy(tmp + 32, dev_rand, 16);
+
+       return aes_cmac_one(zero, tmp, sizeof(tmp), prov_salt);
+}
+
+bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t conf_key[16])
+{
+       const uint8_t prck[4] = "prck";
+
+       if (!aes_cmac_one(salt, secret, 32, conf_key))
+               return false;
+
+       return aes_cmac_one(conf_key, prck, 4, conf_key);
+}
+
+bool mesh_crypto_device_key(const uint8_t secret[32],
+                                               const uint8_t salt[16],
+                                               uint8_t device_key[16])
+{
+       const uint8_t prdk[4] = "prdk";
+
+       if (!aes_cmac_one(salt, secret, 32, device_key))
+               return false;
+
+       return aes_cmac_one(device_key, prdk, 4, device_key);
+}
+
+bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
+                                               uint16_t *addr)
+{
+       uint8_t tmp[16];
+
+       if (!mesh_crypto_s1("vtad", 4, tmp))
+               return false;
+
+       if (!addr || !aes_cmac_one(tmp, virtual_label, 16, tmp))
+               return false;
+
+       *addr = (get_be16(tmp + 14) & 0x3fff) | 0x8000;
+
+       return true;
+}
+
+bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
+                               const uint8_t network_key[16],
+                               uint32_t iv_index,
+                               const uint8_t privacy_key[16])
+{
+       uint8_t network_nonce[13] = { 0x00, 0x00 };
+       uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
+       uint8_t tmp[16];
+       int i;
+
+       /* Detect Proxy packet by CTL == true && DST == 0x0000 */
+       if ((packet[1] & CTL) && get_be16(packet + 7) == 0)
+               network_nonce[0] = 0x03;
+       else
+               /* CTL + TTL */
+               network_nonce[1] = packet[1];
+
+       /* Seq Num */
+       network_nonce[2] = packet[2];
+       network_nonce[3] = packet[3];
+       network_nonce[4] = packet[4];
+
+       /* SRC */
+       network_nonce[5] = packet[5];
+       network_nonce[6] = packet[6];
+
+       /* DST not available */
+       network_nonce[7] = 0;
+       network_nonce[8] = 0;
+
+       /* IV Index */
+       put_be32(iv_index, network_nonce + 9);
+
+       /* Check for Long net-MIC */
+       if (packet[1] & CTL) {
+               if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
+                                       NULL, 0,
+                                       packet + 7, packet_len - 7 - 8,
+                                       packet + 7, NULL, sizeof(uint64_t)))
+                       return false;
+       } else {
+               if (!mesh_crypto_aes_ccm_encrypt(network_nonce, network_key,
+                                       NULL, 0,
+                                       packet + 7, packet_len - 7 - 4,
+                                       packet + 7, NULL, sizeof(uint32_t)))
+                       return false;
+       }
+
+       put_be32(iv_index, privacy_counter + 5);
+       memcpy(privacy_counter + 9, packet + 7, 7);
+
+       if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
+               return false;
+
+       for (i = 0; i < 6; i++)
+               packet[1 + i] ^= tmp[i];
+
+       return true;
+}
+
+bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
+                               bool proxy, uint8_t *out, uint32_t iv_index,
+                               const uint8_t network_key[16],
+                               const uint8_t privacy_key[16])
+{
+       uint8_t privacy_counter[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, };
+       uint8_t network_nonce[13] = { 0x00, 0x00, };
+       uint8_t tmp[16];
+       uint16_t src;
+       int i;
+
+       if (packet_len < 14)
+               return false;
+
+       put_be32(iv_index, privacy_counter + 5);
+       memcpy(privacy_counter + 9, packet + 7, 7);
+
+       if (!aes_ecb_one(privacy_key, privacy_counter, tmp))
+               return false;
+
+       memcpy(out, packet, packet_len);
+       for (i = 0; i < 6; i++)
+               out[1 + i] ^= tmp[i];
+
+       src  = get_be16(out + 5);
+
+       /* Pre-check SRC address for illegal values */
+       if (!src || src >= 0x8000)
+               return false;
+
+       /* Detect Proxy packet by CTL == true && proxy == true */
+       if ((out[1] & CTL) && proxy)
+               network_nonce[0] = 0x03;
+       else
+               /* CTL + TTL */
+               network_nonce[1] = out[1];
+
+       /* Seq Num */
+       network_nonce[2] = out[2];
+       network_nonce[3] = out[3];
+       network_nonce[4] = out[4];
+
+       /* SRC */
+       network_nonce[5] = out[5];
+       network_nonce[6] = out[6];
+
+       /* DST not available */
+       network_nonce[7] = 0;
+       network_nonce[8] = 0;
+
+       /* IV Index */
+       put_be32(iv_index, network_nonce + 9);
+
+       /* Check for Long MIC */
+       if (out[1] & CTL) {
+               uint64_t mic;
+
+               if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
+                                       NULL, 0, packet + 7, packet_len - 7,
+                                       out + 7, &mic, sizeof(mic)))
+                       return false;
+
+               mic ^= get_be64(out + packet_len - 8);
+               put_be64(mic, out + packet_len - 8);
+
+               if (mic)
+                       return false;
+       } else {
+               uint32_t mic;
+
+               if (!mesh_crypto_aes_ccm_decrypt(network_nonce, network_key,
+                                       NULL, 0, packet + 7, packet_len - 7,
+                                       out + 7, &mic, sizeof(mic)))
+                       return false;
+
+               mic ^= get_be32(out + packet_len - 4);
+               put_be32(mic, out + packet_len - 4);
+
+               if (mic)
+                       return false;
+       }
+
+       return true;
+}
+
+bool mesh_get_random_bytes(void *buf, size_t num_bytes)
+{
+       ssize_t len;
+       int fd;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0)
+               return false;
+
+       len = read(fd, buf, num_bytes);
+
+       close(fd);
+
+       if (len < 0)
+               return false;
+
+       return true;
+}
diff --git a/mesh/crypto.h b/mesh/crypto.h
new file mode 100644 (file)
index 0000000..e8f16f3
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 <stdbool.h>
+#include <stdint.h>
+
+bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],
+                                       const uint8_t *aad, uint16_t aad_len,
+                                       const uint8_t *msg, uint16_t msg_len,
+                                       uint8_t *out_msg, void *out_mic,
+                                       size_t mic_size);
+bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],
+                               const uint8_t *aad, uint16_t aad_len,
+                               const uint8_t *enc_msg, uint16_t enc_msg_len,
+                               uint8_t *out_msg, void *out_mic,
+                               size_t mic_size);
+bool mesh_crypto_nkik(const uint8_t network_key[16], uint8_t identity_key[16]);
+bool mesh_crypto_nkbk(const uint8_t network_key[16], uint8_t beacon_key[16]);
+bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,
+                                                       uint8_t id[16]);
+bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,
+                                                       uint8_t id[16]);
+bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],
+                               const uint8_t network_id[16],
+                               uint32_t iv_index, bool kr, bool iu,
+                               uint64_t *cmac);
+bool mesh_crypto_network_nonce(bool frnd, uint8_t ttl, uint32_t seq,
+                               uint16_t src, uint32_t iv_index,
+                               uint8_t nonce[13]);
+bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,
+                               uint32_t seq, uint16_t src,
+                               uint32_t iv_index,
+                               const uint8_t net_key[16],
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *net_mic);
+bool mesh_crypto_network_decrypt(bool frnd, uint8_t ttl,
+                               uint32_t seq, uint16_t src,
+                               uint32_t iv_index,
+                               const uint8_t net_key[16],
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *net_mic, size_t mic_size);
+bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,
+                               uint16_t dst, uint32_t iv_index,
+                               bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,
+                               uint16_t dst, uint32_t iv_index,
+                               bool aszmic, uint8_t nonce[13]);
+bool mesh_crypto_application_encrypt(uint8_t akf, uint32_t seq, uint16_t src,
+                                       uint16_t dst, uint32_t iv_index,
+                                       const uint8_t app_key[16],
+                                       const uint8_t *aad, uint8_t aad_len,
+                                       const uint8_t *msg, uint8_t msg_len,
+                                       uint8_t *out, void *app_mic,
+                                       size_t mic_size);
+bool mesh_crypto_application_decrypt(uint8_t akf, uint32_t seq, uint16_t src,
+                               uint16_t dst, uint32_t iv_index,
+                               const uint8_t app_key[16],
+                               const uint8_t *aad, uint8_t aad_len,
+                               const uint8_t *enc_msg, uint8_t enc_msg_len,
+                               uint8_t *out, void *app_mic, size_t mic_size);
+bool mesh_crypto_device_key(const uint8_t secret[32],
+                                               const uint8_t salt[16],
+                                               uint8_t device_key[16]);
+bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],
+                                               uint16_t *v_addr);
+bool mesh_crypto_nonce(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t nonce[13]);
+bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],
+               const void *info, size_t info_len, uint8_t okm[16]);
+bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,
+                                                       uint8_t net_id[1],
+                                                       uint8_t enc_key[16],
+                                                       uint8_t priv_key[16]);
+bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8]);
+bool mesh_crypto_k4(const uint8_t a[16], uint8_t out5[1]);
+bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16]);
+bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],
+                                       const uint8_t prov_rand[16],
+                                       const uint8_t dev_rand[16],
+                                       uint8_t prov_salt[16]);
+bool mesh_crypto_prov_conf_key(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t conf_key[16]);
+bool mesh_crypto_session_key(const uint8_t secret[32],
+                                       const uint8_t salt[16],
+                                       uint8_t session_key[16]);
+bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,
+                               const uint8_t network_key[16],
+                               uint32_t iv_index,
+                               const uint8_t privacy_key[16]);
+bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,
+                               bool proxy, uint8_t *out, uint32_t iv_index,
+                               const uint8_t network_key[16],
+                               const uint8_t privacy_key[16]);
+
+bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,
+                                       size_t msg_len, uint8_t res[16]);
+
+bool mesh_get_random_bytes(void *buf, size_t num_bytes);
diff --git a/mesh/gatt.c b/mesh/gatt.c
new file mode 100644 (file)
index 0000000..9116a9d
--- /dev/null
@@ -0,0 +1,519 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <glib.h>
+
+#include "src/shared/io.h"
+#include "src/shared/shell.h"
+#include "gdbus/gdbus.h"
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+#include "mesh/gatt.h"
+#include "mesh/prov.h"
+#include "mesh/net.h"
+
+#define MESH_PROV_DATA_OUT_UUID_STR    "00002adc-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_OUT_UUID_STR   "00002ade-0000-1000-8000-00805f9b34fb"
+
+static struct io *write_io;
+static uint16_t write_mtu;
+
+static struct io *notify_io;
+static uint16_t notify_mtu;
+
+struct write_data {
+       GDBusProxy *proxy;
+       void *user_data;
+       struct iovec iov;
+       GDBusReturnFunction cb;
+       uint8_t *gatt_data;
+       uint8_t gatt_len;
+};
+
+struct notify_data {
+       GDBusProxy *proxy;
+       bool enable;
+       GDBusReturnFunction cb;
+       void *user_data;
+};
+
+bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,
+                       const char *name)
+{
+       DBusMessageIter iter;
+       const char *parent_path;
+
+       if (!parent)
+               return FALSE;
+
+       if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&iter, &parent_path);
+
+       if (!strcmp(parent_path, g_dbus_proxy_get_path(parent)))
+               return TRUE;
+       else
+               return FALSE;
+}
+
+/* Refactor this once actual MTU is available */
+#define GATT_MTU       23
+
+static void write_data_free(void *user_data)
+{
+       struct write_data *data = user_data;
+
+       g_free(data->gatt_data);
+       free(data);
+}
+
+uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)
+{
+       const uint8_t *data = *pkt;
+       uint8_t gatt_hdr = *data++;
+       uint8_t type = gatt_hdr & GATT_TYPE_MASK;
+       static uint8_t gatt_size;
+       static uint8_t gatt_pkt[67];
+
+       print_byte_array("GATT-RX:\t", *pkt, size);
+       if (size < 1) {
+               gatt_pkt[0] = GATT_TYPE_INVALID;
+               /* TODO: Disconnect GATT per last paragraph sec 6.6 */
+               return 0;
+       }
+
+       size--;
+
+       switch (gatt_hdr & GATT_SAR_MASK) {
+       case GATT_SAR_FIRST:
+               gatt_size = 1;
+               gatt_pkt[0] = type;
+               /* TODO: Start Proxy Timeout */
+               /* fall through */
+
+       case GATT_SAR_CONTINUE:
+               if (gatt_pkt[0] != type ||
+                               gatt_size + size > MAX_GATT_SIZE) {
+
+                       /* Invalidate packet and return failure */
+                       gatt_pkt[0] = GATT_TYPE_INVALID;
+                       /* TODO: Disconnect GATT per last paragraph sec 6.6 */
+                       return 0;
+               }
+
+               memcpy(gatt_pkt + gatt_size, data, size);
+               gatt_size += size;
+
+               /* We are good to this point, but incomplete */
+               return 0;
+
+       default:
+       case GATT_SAR_COMPLETE:
+               gatt_size = 1;
+               gatt_pkt[0] = type;
+
+               /* fall through */
+
+       case GATT_SAR_LAST:
+               if (gatt_pkt[0] != type ||
+                               gatt_size + size > MAX_GATT_SIZE) {
+
+                       /* Invalidate packet and return failure */
+                       gatt_pkt[0] = GATT_TYPE_INVALID;
+                       /* Disconnect GATT per last paragraph sec 6.6 */
+                       return 0;
+               }
+
+               memcpy(gatt_pkt + gatt_size, data, size);
+               gatt_size += size;
+               *pkt = gatt_pkt;
+               return gatt_size;
+       }
+}
+
+static bool pipe_write(struct io *io, void *user_data)
+{
+       struct write_data *data = user_data;
+       struct iovec iov[2];
+       uint8_t sar;
+       uint8_t max_len;
+
+       if (data == NULL)
+               return true;
+
+       max_len = write_mtu ? write_mtu - 3 - 1 : GATT_MTU - 3 - 1;
+       print_byte_array("GATT-TX:\t", data->gatt_data, data->gatt_len);
+
+       sar = data->gatt_data[0];
+
+       data->iov.iov_base = data->gatt_data + 1;
+       data->iov.iov_len--;
+
+       sar = data->gatt_data[0] & GATT_TYPE_MASK;
+       data->gatt_len--;
+
+       if (data->gatt_len > max_len) {
+               sar |= GATT_SAR_FIRST;
+               data->iov.iov_len = max_len;
+       }
+
+       iov[0].iov_base = &sar;
+       iov[0].iov_len = sizeof(sar);
+
+       while (1) {
+               int err;
+
+               iov[1] = data->iov;
+
+               err = io_send(io, iov, 2);
+               if (err < 0) {
+                       bt_shell_printf("Failed to write: %s\n", strerror(-err));
+                       write_data_free(data);
+                       return false;
+               }
+
+               switch (sar & GATT_SAR_MASK) {
+               case GATT_SAR_FIRST:
+               case GATT_SAR_CONTINUE:
+                       data->gatt_len -= max_len;
+                       data->iov.iov_base = data->iov.iov_base + max_len;
+
+                       sar &= GATT_TYPE_MASK;
+                       if (max_len < data->gatt_len) {
+                               data->iov.iov_len = max_len;
+                               sar |= GATT_SAR_CONTINUE;
+                       } else {
+                               data->iov.iov_len = data->gatt_len;
+                               sar |= GATT_SAR_LAST;
+                       }
+
+                       break;
+
+               default:
+                       if(data->cb)
+                               data->cb(NULL, data->user_data);
+                       write_data_free(data);
+                       return true;
+               }
+       }
+}
+
+static void write_io_destroy(void)
+{
+       io_destroy(write_io);
+       write_io = NULL;
+       write_mtu = 0;
+}
+
+static void notify_io_destroy(void)
+{
+       io_destroy(notify_io);
+       notify_io = NULL;
+       notify_mtu = 0;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+       bt_shell_printf("%s closed\n", io == notify_io ? "Notify" : "Write");
+
+       if (io == notify_io)
+               notify_io_destroy();
+       else
+               write_io_destroy();
+
+       return false;
+}
+
+static struct io *pipe_io_new(int fd)
+{
+       struct io *io;
+
+       io = io_new(fd);
+
+       io_set_close_on_destroy(io, true);
+
+       io_set_disconnect_handler(io, pipe_hup, NULL, NULL);
+
+       return io;
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+       struct write_data *data = user_data;
+       DBusError error;
+       int fd;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               dbus_error_free(&error);
+               bt_shell_printf("Failed to write\n");
+               write_data_free(data);
+               return;
+       }
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &write_mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               bt_shell_printf("Invalid AcquireWrite response\n");
+               return;
+       }
+
+       bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd, write_mtu);
+
+       write_io = pipe_io_new(fd);
+
+       pipe_write(write_io, data);
+}
+
+static void acquire_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);
+}
+
+bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
+                       GDBusReturnFunction cb, void *user_data)
+{
+       struct write_data *data;
+
+       if (!buf || !len)
+               return false;
+
+       if (len > 69)
+               return false;
+
+       data = g_new0(struct write_data, 1);
+       if (!data)
+               return false;
+
+       /* TODO: should keep in queue in case we need to cancel write? */
+
+       data->gatt_len = len;
+       data->gatt_data = g_memdup(buf, len);
+       data->gatt_data[0] &= GATT_TYPE_MASK;
+       data->iov.iov_base = data->gatt_data;
+       data->iov.iov_len = len;
+       data->proxy = proxy;
+       data->user_data = user_data;
+       data->cb = cb;
+
+       if (write_io)
+               return pipe_write(write_io, data);
+
+       if (g_dbus_proxy_method_call(proxy, "AcquireWrite",
+                               acquire_setup, acquire_write_reply,
+                               data, NULL) == FALSE) {
+               bt_shell_printf("Failed to AcquireWrite\n");
+               write_data_free(data);
+               return false;
+       }
+       return true;
+}
+
+static void notify_reply(DBusMessage *message, void *user_data)
+{
+       struct notify_data *data = user_data;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to %s notify: %s\n",
+                               data->enable ? "start" : "stop", error.name);
+               dbus_error_free(&error);
+               goto done;
+       }
+
+       bt_shell_printf("Notify %s\n", data->enable ? "started" : "stopped");
+
+done:
+       if (data->cb)
+               data->cb(message, data->user_data);
+
+       g_free(data);
+}
+
+static bool pipe_read(struct io *io, bool prov, void *user_data)
+{
+       struct mesh_node *node = user_data;
+       uint8_t buf[512];
+       uint8_t *res;
+       int fd = io_get_fd(io);
+       ssize_t len;
+
+       if (io != notify_io)
+               return true;
+
+       while ((len = read(fd, buf, sizeof(buf)))) {
+               if (len <= 0)
+                       break;
+
+               res = buf;
+               mesh_gatt_sar(&res, len);
+
+               if (prov)
+                       prov_data_ready(node, res, len);
+               else
+                       net_data_ready(res, len);
+       }
+
+       return true;
+}
+
+static bool pipe_read_prov(struct io *io, void *user_data)
+{
+       return pipe_read(io, true, user_data);
+}
+
+static bool pipe_read_proxy(struct io *io, void *user_data)
+{
+       return pipe_read(io, false, user_data);
+}
+
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
+{
+       struct notify_data *data = user_data;
+       DBusMessageIter iter;
+       DBusError error;
+       int fd;
+       const char *uuid;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               dbus_error_free(&error);
+               if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
+                                       notify_reply, data, NULL) == FALSE) {
+                       bt_shell_printf("Failed to StartNotify\n");
+                       g_free(data);
+               }
+               return;
+       }
+
+       if (notify_io) {
+               io_destroy(notify_io);
+               notify_io = NULL;
+       }
+
+       notify_mtu = 0;
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &notify_mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               if (g_dbus_proxy_method_call(data->proxy, "StartNotify", NULL,
+                                       notify_reply, data, NULL) == FALSE) {
+                       bt_shell_printf("Failed to StartNotify\n");
+                       g_free(data);
+               }
+               return;
+       }
+
+       bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd, notify_mtu);
+
+       if (g_dbus_proxy_get_property(data->proxy, "UUID", &iter) == FALSE)
+               goto done;
+
+       notify_io = pipe_io_new(fd);
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR))
+               io_set_read_handler(notify_io, pipe_read_prov, data->user_data,
+                                                                       NULL);
+       else if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+               io_set_read_handler(notify_io, pipe_read_proxy, data->user_data,
+                                                                       NULL);
+
+done:
+       if (data->cb)
+               data->cb(message, data->user_data);
+
+       g_free(data);
+}
+
+bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
+                       void *user_data)
+{
+       struct notify_data *data;
+       DBusMessageIter iter;
+       const char *method;
+       GDBusSetupFunction setup = NULL;
+
+       data = g_new0(struct notify_data, 1);
+       data->proxy = proxy;
+       data->enable = enable;
+       data->cb = cb;
+       data->user_data = user_data;
+
+       if (enable == TRUE) {
+               if (g_dbus_proxy_get_property(proxy, "NotifyAcquired", &iter)) {
+                       method = "AcquireNotify";
+                       cb = acquire_notify_reply;
+                       setup = acquire_setup;
+               } else {
+                       method = "StartNotify";
+                       cb = notify_reply;
+               }
+       } else {
+               if (notify_io) {
+                       notify_io_destroy();
+                       if (cb)
+                               cb(NULL, user_data);
+                       return true;
+               } else {
+                       method = "StopNotify";
+                       cb = notify_reply;
+               }
+       }
+
+       if (g_dbus_proxy_method_call(proxy, method, setup, cb,
+                                       data, NULL) == FALSE) {
+               bt_shell_printf("Failed to %s\n", method);
+               return false;
+       }
+       return true;
+}
diff --git a/mesh/gatt.h b/mesh/gatt.h
new file mode 100644 (file)
index 0000000..2878587
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 "gdbus/gdbus.h"
+
+/* Largest Possible GATT Packet: Provisioning Public Key + type + sar */
+#define MAX_GATT_SIZE  (64 + 1 + 1)
+
+#define GATT_SAR_MASK          0xc0
+#define GATT_SAR_COMPLETE      0x00
+#define GATT_SAR_FIRST         0x40
+#define GATT_SAR_CONTINUE      0x80
+#define GATT_SAR_LAST          0xc0
+#define GATT_TYPE_INVALID      0xff
+#define GATT_TYPE_MASK         0x3f
+
+uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size);
+bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,
+                       const char *name);
+bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,
+                       GDBusReturnFunction cb, void *user_data);
+bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,
+                       void *user_data);
+void mesh_gatt_cleanup(void);
diff --git a/mesh/keys.h b/mesh/keys.h
new file mode 100644 (file)
index 0000000..477ff13
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#define KR_PHASE_NONE          0x00
+#define KR_PHASE_ONE           0x01
+#define KR_PHASE_TWO           0x02
+#define KR_PHASE_INVALID       0xff
+
+bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
+                     bool update);
+bool keys_net_key_add(uint16_t index, uint8_t *key, bool update);
+uint16_t keys_app_key_get_bound(uint16_t app_idx);
+uint8_t *keys_app_key_get(uint16_t app_idx, bool current);
+uint8_t *keys_net_key_get(uint16_t net_idx, bool current);
+bool keys_app_key_delete(uint16_t app_idx);
+bool keys_net_key_delete(uint16_t net_idx);
+uint8_t keys_get_kr_phase(uint16_t net_idx);
+bool keys_set_kr_phase(uint16_t index, uint8_t phase);
+void keys_cleanup_all(void);
diff --git a/mesh/local_node.json b/mesh/local_node.json
new file mode 100644 (file)
index 0000000..5ffa7ad
--- /dev/null
@@ -0,0 +1,61 @@
+{
+  "$schema":"file:\/\/\/BlueZ\/Mesh\/local_schema\/mesh.jsonschema",
+  "meshName":"BT Mesh",
+  "netKeys":[
+    {
+      "index": 0,
+      "keyRefresh": 0
+    }
+  ],
+  "appKeys":[
+    {
+      "index": 0,
+      "boundNetKey": 0
+    },
+    {
+      "index": 1,
+      "boundNetKey": 0
+    }
+  ],
+"node": {
+       "IVindex":"00000005",
+       "IVupdate":"0",
+       "sequenceNumber": 0,
+    "composition": {
+        "cid": "0002",
+        "pid": "0010",
+        "vid": "0001",
+        "crpl": "000a",
+        "features": {
+            "relay": false,
+            "proxy": true,
+            "friend": false,
+            "lowPower": false
+        },
+        "elements": [
+            {
+                "elementIndex": 0,
+                "location": "0001",
+                "models": ["0000", "0001", "1001"]
+            }
+        ]
+    },
+    "configuration":{
+        "netKeys": [0],
+        "appKeys": [ 0, 1],
+        "defaultTTL": 10,
+        "elements": [
+          {
+            "elementIndex": 0,
+            "unicastAddress":"0077",
+            "models": [
+               {
+                 "modelId": "1001",
+                 "bind": [1]
+                }
+            ]
+          }
+        ]
+    }
+  }
+}
diff --git a/mesh/main.c b/mesh/main.c
new file mode 100644 (file)
index 0000000..b5ec4bc
--- /dev/null
@@ -0,0 +1,2046 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <wordexp.h>
+
+#include <inttypes.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include "bluetooth/bluetooth.h"
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "gdbus/gdbus.h"
+#include "mesh/mesh-net.h"
+#include "mesh/gatt.h"
+#include "mesh/crypto.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/keys.h"
+#include "mesh/prov.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+#include "mesh/prov-db.h"
+#include "mesh/config-model.h"
+#include "mesh/onoff-model.h"
+
+/* String display constants */
+#define COLORED_NEW    COLOR_GREEN "NEW" COLOR_OFF
+#define COLORED_CHG    COLOR_YELLOW "CHG" COLOR_OFF
+#define COLORED_DEL    COLOR_RED "DEL" COLOR_OFF
+
+#define PROMPT_ON      COLOR_BLUE "[meshctl]" COLOR_OFF "# "
+#define PROMPT_OFF     "Waiting to connect to bluetoothd..."
+
+#define MESH_PROV_DATA_IN_UUID_STR     "00002adb-0000-1000-8000-00805f9b34fb"
+#define MESH_PROV_DATA_OUT_UUID_STR    "00002adc-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_IN_UUID_STR    "00002add-0000-1000-8000-00805f9b34fb"
+#define MESH_PROXY_DATA_OUT_UUID_STR   "00002ade-0000-1000-8000-00805f9b34fb"
+
+static DBusConnection *dbus_conn;
+
+struct adapter {
+GDBusProxy *proxy;
+       GList *mesh_devices;
+};
+
+struct mesh_device {
+       GDBusProxy *proxy;
+       uint8_t dev_uuid[16];
+       gboolean hide;
+};
+
+GList *service_list;
+GList *char_list;
+
+static GList *ctrl_list;
+static struct adapter *default_ctrl;
+
+static char *mesh_prov_db_filename;
+static char *mesh_local_config_filename;
+
+static bool discovering = false;
+static bool discover_mesh;
+static uint16_t prov_net_key_index = NET_IDX_PRIMARY;
+static const struct bt_shell_menu main_menu;
+
+#define CONN_TYPE_NETWORK      0x00
+#define CONN_TYPE_IDENTITY     0x01
+#define CONN_TYPE_PROVISION    0x02
+#define CONN_TYPE_INVALID      0xff
+
+#define NET_IDX_INVALID                0xffff
+
+struct {
+       GDBusProxy *device;
+       GDBusProxy *service;
+       GDBusProxy *data_in;
+       GDBusProxy *data_out;
+       bool session_open;
+       uint16_t unicast;
+       uint16_t net_idx;
+       uint8_t dev_uuid[16];
+       uint8_t type;
+} connection;
+
+static bool service_is_mesh(GDBusProxy *proxy, const char *target_uuid)
+{
+       DBusMessageIter iter;
+       const char *uuid;
+
+       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+               return false;
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       if (target_uuid)
+               return (!bt_uuid_strcmp(uuid, target_uuid));
+       else if (bt_uuid_strcmp(uuid, MESH_PROV_SVC_UUID) ||
+                               bt_uuid_strcmp(uuid, MESH_PROXY_SVC_UUID))
+               return true;
+       else
+               return false;
+}
+
+static bool char_is_mesh(GDBusProxy *proxy, const char *target_uuid)
+{
+       DBusMessageIter iter;
+       const char *uuid;
+
+       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+               return false;
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       if (target_uuid)
+               return (!bt_uuid_strcmp(uuid, target_uuid));
+
+       if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_IN_UUID_STR))
+               return true;
+
+       if (!bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR))
+               return true;
+
+       if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_IN_UUID_STR))
+               return true;
+
+       if (!bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+               return true;
+
+       return false;
+}
+
+static gboolean check_default_ctrl(void)
+{
+       if (!default_ctrl) {
+               bt_shell_printf("No default controller available\n");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void proxy_leak(gpointer data)
+{
+       bt_shell_printf("Leaking proxy %p\n", data);
+}
+
+static void connect_handler(DBusConnection *connection, void *user_data)
+{
+       bt_shell_set_prompt(PROMPT_ON);
+}
+
+static void disconnect_handler(DBusConnection *connection, void *user_data)
+{
+       bt_shell_detach();
+
+       bt_shell_set_prompt(PROMPT_OFF);
+
+       g_list_free_full(ctrl_list, proxy_leak);
+       ctrl_list = NULL;
+
+       default_ctrl = NULL;
+}
+
+static void print_adapter(GDBusProxy *proxy, const char *description)
+{
+       DBusMessageIter iter;
+       const char *address, *name;
+
+       if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &address);
+
+       if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
+               dbus_message_iter_get_basic(&iter, &name);
+       else
+               name = "<unknown>";
+
+       bt_shell_printf("%s%s%sController %s %s %s\n",
+                               description ? "[" : "",
+                               description ? : "",
+                               description ? "] " : "",
+                               address, name,
+                               default_ctrl &&
+                               default_ctrl->proxy == proxy ?
+                               "[default]" : "");
+
+}
+
+static void print_device(GDBusProxy *proxy, const char *description)
+{
+       DBusMessageIter iter;
+       const char *address, *name;
+
+       if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &address);
+
+       if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
+               dbus_message_iter_get_basic(&iter, &name);
+       else
+               name = "<unknown>";
+
+       bt_shell_printf("%s%s%sDevice %s %s\n",
+                               description ? "[" : "",
+                               description ? : "",
+                               description ? "] " : "",
+                               address, name);
+}
+
+static void print_iter(const char *label, const char *name,
+                                               DBusMessageIter *iter)
+{
+       dbus_bool_t valbool;
+       dbus_uint32_t valu32;
+       dbus_uint16_t valu16;
+       dbus_int16_t vals16;
+       unsigned char byte;
+       const char *valstr;
+       DBusMessageIter subiter;
+       char *entry;
+
+       if (iter == NULL) {
+               bt_shell_printf("%s%s is nil\n", label, name);
+               return;
+       }
+
+       switch (dbus_message_iter_get_arg_type(iter)) {
+       case DBUS_TYPE_INVALID:
+               bt_shell_printf("%s%s is invalid\n", label, name);
+               break;
+       case DBUS_TYPE_STRING:
+       case DBUS_TYPE_OBJECT_PATH:
+               dbus_message_iter_get_basic(iter, &valstr);
+               bt_shell_printf("%s%s: %s\n", label, name, valstr);
+               break;
+       case DBUS_TYPE_BOOLEAN:
+               dbus_message_iter_get_basic(iter, &valbool);
+               bt_shell_printf("%s%s: %s\n", label, name,
+                                       valbool == TRUE ? "yes" : "no");
+               break;
+       case DBUS_TYPE_UINT32:
+               dbus_message_iter_get_basic(iter, &valu32);
+               bt_shell_printf("%s%s: 0x%06x\n", label, name, valu32);
+               break;
+       case DBUS_TYPE_UINT16:
+               dbus_message_iter_get_basic(iter, &valu16);
+               bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
+               break;
+       case DBUS_TYPE_INT16:
+               dbus_message_iter_get_basic(iter, &vals16);
+               bt_shell_printf("%s%s: %d\n", label, name, vals16);
+               break;
+       case DBUS_TYPE_BYTE:
+               dbus_message_iter_get_basic(iter, &byte);
+               bt_shell_printf("%s%s: 0x%02x\n", label, name, byte);
+               break;
+       case DBUS_TYPE_VARIANT:
+               dbus_message_iter_recurse(iter, &subiter);
+               print_iter(label, name, &subiter);
+               break;
+       case DBUS_TYPE_ARRAY:
+               dbus_message_iter_recurse(iter, &subiter);
+               while (dbus_message_iter_get_arg_type(&subiter) !=
+                                                       DBUS_TYPE_INVALID) {
+                       print_iter(label, name, &subiter);
+                       dbus_message_iter_next(&subiter);
+               }
+               break;
+       case DBUS_TYPE_DICT_ENTRY:
+               dbus_message_iter_recurse(iter, &subiter);
+               entry = g_strconcat(name, "Key", NULL);
+               print_iter(label, entry, &subiter);
+               g_free(entry);
+
+               entry = g_strconcat(name, " Value", NULL);
+               dbus_message_iter_next(&subiter);
+               print_iter(label, entry, &subiter);
+               g_free(entry);
+               break;
+       default:
+               bt_shell_printf("%s%s has unsupported type\n", label, name);
+               break;
+       }
+}
+
+static void print_property(GDBusProxy *proxy, const char *name)
+{
+       DBusMessageIter iter;
+
+       if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
+               return;
+
+       print_iter("\t", name, &iter);
+}
+
+static void forget_mesh_devices()
+{
+       g_list_free_full(default_ctrl->mesh_devices, g_free);
+       default_ctrl->mesh_devices = NULL;
+}
+
+static struct mesh_device *find_device_by_uuid(GList *source, uint8_t uuid[16])
+{
+       GList *list;
+
+       for (list = g_list_first(source); list; list = g_list_next(list)) {
+               struct mesh_device *dev = list->data;
+
+               if (!memcmp(dev->dev_uuid, uuid, 16))
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static void print_prov_service(struct prov_svc_data *prov_data)
+{
+       const char *prefix = "\t\t";
+       char txt_uuid[16 * 2 + 1];
+       int i;
+
+       bt_shell_printf("%sMesh Provisioning Service (%s)\n", prefix,
+                                                       MESH_PROV_SVC_UUID);
+       for (i = 0; i < 16; ++i) {
+               sprintf(txt_uuid + (i * 2), "%2.2x", prov_data->dev_uuid[i]);
+       }
+
+       bt_shell_printf("%s\tDevice UUID: %s\n", prefix, txt_uuid);
+       bt_shell_printf("%s\tOOB: %4.4x\n", prefix, prov_data->oob);
+
+}
+
+static bool parse_prov_service_data(const char *uuid, uint8_t *data, int len,
+                                                               void *data_out)
+{
+       struct prov_svc_data *prov_data = data_out;
+       int i;
+
+       if (len < 18)
+               return false;
+
+       for (i = 0; i < 16; ++i) {
+               prov_data->dev_uuid[i] = data[i];
+       }
+
+       prov_data->oob = get_be16(&data[16]);
+
+       return true;
+}
+
+static bool parse_mesh_service_data(const char *uuid, uint8_t *data, int len,
+                                                               void *data_out)
+{
+       const char *prefix = "\t\t";
+
+       if (!(len == 9 && data[0] == 0x00) && !(len == 17 && data[0] == 0x01)) {
+               bt_shell_printf("Unexpected mesh proxy service data length %d\n",
+                                                                       len);
+               return false;
+       }
+
+       if (data[0] != connection.type)
+               return false;
+
+       if (data[0] == CONN_TYPE_IDENTITY) {
+               uint8_t *key;
+
+               if (IS_UNASSIGNED(connection.unicast)) {
+                       /* This would be a bug */
+                       bt_shell_printf("Error: Searching identity with "
+                                                       "unicast 0000\n");
+                       return false;
+               }
+
+               key = keys_net_key_get(prov_net_key_index, true);
+               if (!key)
+                       return false;
+
+               if (!mesh_crypto_identity_check(key, connection.unicast,
+                                              &data[1]))
+                       return false;
+
+               if (discovering) {
+                       bt_shell_printf("\n%sMesh Proxy Service (%s)\n", prefix,
+                                                                       uuid);
+                       bt_shell_printf("%sIdentity for node %4.4x\n", prefix,
+                                                       connection.unicast);
+               }
+
+       } else if (data[0] == CONN_TYPE_NETWORK) {
+               uint16_t net_idx = net_validate_proxy_beacon(data + 1);
+
+               if (net_idx == NET_IDX_INVALID || net_idx != connection.net_idx)
+                       return false;
+
+               if (discovering) {
+                       bt_shell_printf("\n%sMesh Proxy Service (%s)\n", prefix,
+                                                                       uuid);
+                       bt_shell_printf("%sNetwork Beacon for net index %4.4x\n",
+                                                       prefix, net_idx);
+               }
+       }
+
+       return true;
+}
+
+static bool parse_service_data(GDBusProxy *proxy, const char *target_uuid,
+                                       void *data_out)
+{
+       DBusMessageIter iter, entries;
+       bool mesh_prov = false;
+       bool mesh_proxy = false;
+
+       if (target_uuid) {
+               mesh_prov = !strcmp(target_uuid, MESH_PROV_SVC_UUID);
+               mesh_proxy = !strcmp(target_uuid, MESH_PROXY_SVC_UUID);
+       }
+
+       if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
+               return false;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+               return false;
+
+       dbus_message_iter_recurse(&iter, &entries);
+
+       while (dbus_message_iter_get_arg_type(&entries)
+                                               == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter value, entry, array;
+               const char *uuid_str;
+               bt_uuid_t uuid;
+               uint8_t *service_data;
+               int len;
+
+               dbus_message_iter_recurse(&entries, &entry);
+               dbus_message_iter_get_basic(&entry, &uuid_str);
+
+               if (bt_string_to_uuid(&uuid, uuid_str) < 0)
+                       goto fail;
+
+               dbus_message_iter_next(&entry);
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       goto fail;
+
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
+                       goto fail;
+
+               dbus_message_iter_recurse(&value, &array);
+
+               if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+                       goto fail;
+
+               dbus_message_iter_get_fixed_array(&array, &service_data, &len);
+
+               if (mesh_prov && !strcmp(uuid_str, MESH_PROV_SVC_UUID)) {
+                       return parse_prov_service_data(uuid_str, service_data,
+                                                               len, data_out);
+               } else if (mesh_proxy &&
+                               !strcmp(uuid_str, MESH_PROXY_SVC_UUID)) {
+                       return parse_mesh_service_data(uuid_str, service_data,
+                                                               len, data_out);
+               }
+
+               dbus_message_iter_next(&entries);
+       }
+
+       if (!target_uuid)
+               return true;
+fail:
+       return false;
+}
+
+static void print_uuids(GDBusProxy *proxy)
+{
+       DBusMessageIter iter, value;
+
+       if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_recurse(&iter, &value);
+
+       while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
+               const char *uuid, *text;
+
+               dbus_message_iter_get_basic(&value, &uuid);
+
+               text = bt_uuidstr_to_str(uuid);
+               if (text) {
+                       char str[26];
+                       unsigned int n;
+
+                       str[sizeof(str) - 1] = '\0';
+
+                       n = snprintf(str, sizeof(str), "%s", text);
+                       if (n > sizeof(str) - 1) {
+                               str[sizeof(str) - 2] = '.';
+                               str[sizeof(str) - 3] = '.';
+                               if (str[sizeof(str) - 4] == ' ')
+                                       str[sizeof(str) - 4] = '.';
+
+                               n = sizeof(str) - 1;
+                       }
+
+                       bt_shell_printf("\tUUID: %s%*c(%s)\n",
+                                               str, 26 - n, ' ', uuid);
+               } else
+                       bt_shell_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid);
+
+               dbus_message_iter_next(&value);
+       }
+}
+
+static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)
+{
+       DBusMessageIter iter;
+       const char *adapter, *path;
+
+       if (!master)
+               return FALSE;
+
+       if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&iter, &adapter);
+       path = g_dbus_proxy_get_path(master);
+
+       if (!strcmp(path, adapter))
+               return TRUE;
+
+       return FALSE;
+}
+
+static struct adapter *find_parent(GDBusProxy *device)
+{
+       GList *list;
+
+       for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+               struct adapter *adapter = list->data;
+
+               if (device_is_child(device, adapter->proxy) == TRUE)
+                       return adapter;
+       }
+       return NULL;
+}
+
+static void set_connected_device(GDBusProxy *proxy)
+{
+       char *desc = NULL;
+       DBusMessageIter iter;
+       char buf[10];
+       bool mesh;
+
+       connection.device = proxy;
+
+       if (proxy == NULL) {
+               memset(&connection, 0, sizeof(connection));
+               connection.type = CONN_TYPE_INVALID;
+               goto done;
+       }
+
+       if (connection.type == CONN_TYPE_IDENTITY) {
+               mesh = true;
+               snprintf(buf, 10, "Node-%4.4x", connection.unicast);
+       } else if (connection.type == CONN_TYPE_NETWORK) {
+               mesh = true;
+               snprintf(buf, 9, "Net-%4.4x", connection.net_idx);
+       } else {
+               mesh = false;
+       }
+
+       if (!g_dbus_proxy_get_property(proxy, "Alias", &iter) && !mesh)
+                       goto done;
+
+       dbus_message_iter_get_basic(&iter, &desc);
+       desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
+                              (desc && mesh) ? "-" : "",
+                               mesh ? buf : "");
+
+done:
+       bt_shell_set_prompt(desc ? desc : PROMPT_ON);
+       g_free(desc);
+
+       /* If disconnected, return to main menu */
+       if (proxy == NULL)
+               bt_shell_set_menu(&main_menu);
+}
+
+static void connect_reply(DBusMessage *message, void *user_data)
+{
+       GDBusProxy *proxy = user_data;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to connect: %s\n", error.name);
+               dbus_error_free(&error);
+               set_connected_device(NULL);
+               return;
+       }
+
+       bt_shell_printf("Connection successful\n");
+
+       set_connected_device(proxy);
+}
+
+static void update_device_info(GDBusProxy *proxy)
+{
+       struct adapter *adapter = find_parent(proxy);
+       DBusMessageIter iter;
+       struct prov_svc_data prov_data;
+
+       if (!adapter) {
+               /* TODO: Error */
+               return;
+       }
+
+       if (adapter != default_ctrl)
+               return;
+
+       if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
+               return;
+
+       if (parse_service_data(proxy, MESH_PROV_SVC_UUID, &prov_data)) {
+               struct mesh_device *dev;
+
+               dev = find_device_by_uuid(adapter->mesh_devices,
+                                                       prov_data.dev_uuid);
+
+               /* Display provisioning service once per discovery session */
+               if (discovering && (!dev || !dev->hide))
+                                               print_prov_service(&prov_data);
+
+               if (dev) {
+                       dev->proxy = proxy;
+                       dev->hide = discovering;
+                       return;
+               }
+
+               dev = g_malloc0(sizeof(struct mesh_device));
+               if (!dev)
+                       return;
+
+               dev->proxy = proxy;
+               dev->hide = discovering;
+
+               memcpy(dev->dev_uuid, prov_data.dev_uuid, 16);
+
+               adapter->mesh_devices = g_list_append(adapter->mesh_devices,
+                                                       dev);
+               print_device(proxy, COLORED_NEW);
+
+               node_create_new(&prov_data);
+
+       } else if (parse_service_data(proxy, MESH_PROXY_SVC_UUID, NULL) &&
+                                                               discover_mesh) {
+               bool res;
+
+               g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+                                               NULL, NULL, NULL, NULL);
+               discover_mesh = false;
+
+               forget_mesh_devices();
+
+               res = g_dbus_proxy_method_call(proxy, "Connect", NULL,
+                                               connect_reply, proxy, NULL);
+
+               if (!res)
+                       bt_shell_printf("Failed to connect to mesh\n");
+
+               else
+                       bt_shell_printf("Trying to connect to mesh\n");
+
+       }
+}
+
+static void adapter_added(GDBusProxy *proxy)
+{
+       struct adapter *adapter = g_malloc0(sizeof(struct adapter));
+
+       adapter->proxy = proxy;
+       ctrl_list = g_list_append(ctrl_list, adapter);
+
+       if (!default_ctrl)
+               default_ctrl = adapter;
+
+       print_adapter(proxy, COLORED_NEW);
+}
+
+static void data_out_notify(GDBusProxy *proxy, bool enable,
+                               GDBusReturnFunction cb)
+{
+       struct mesh_node *node;
+
+       node = node_find_by_uuid(connection.dev_uuid);
+
+       if (!mesh_gatt_notify(proxy, enable, cb, node))
+               bt_shell_printf("Failed to %s notification on %s\n", enable ?
+                               "start" : "stop", g_dbus_proxy_get_path(proxy));
+       else
+               bt_shell_printf("%s notification on %s\n", enable ?
+                         "Start" : "Stop", g_dbus_proxy_get_path(proxy));
+}
+
+struct disconnect_data {
+       GDBusReturnFunction cb;
+       void *data;
+};
+
+static void disconnect(GDBusReturnFunction cb, void *user_data)
+{
+       GDBusProxy *proxy;
+       DBusMessageIter iter;
+       const char *addr;
+
+       proxy = connection.device;
+       if (!proxy)
+               return;
+
+       if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, cb, user_data,
+                                                       NULL) == FALSE) {
+               bt_shell_printf("Failed to disconnect\n");
+               return;
+       }
+
+       if (g_dbus_proxy_get_property(proxy, "Address", &iter) == TRUE)
+                       dbus_message_iter_get_basic(&iter, &addr);
+
+       bt_shell_printf("Attempting to disconnect from %s\n", addr);
+}
+
+static void disc_notify_cb(DBusMessage *message, void *user_data)
+{
+       struct disconnect_data *disc_data = user_data;
+
+       disconnect(disc_data->cb, disc_data->data);
+
+       g_free(user_data);
+}
+
+static void disconnect_device(GDBusReturnFunction cb, void *user_data)
+{
+       DBusMessageIter iter;
+
+       net_session_close(connection.data_in);
+
+       /* Stop notificiation on prov_out or proxy out characteristics */
+       if (connection.data_out) {
+               if (g_dbus_proxy_get_property(connection.data_out, "Notifying",
+                                                       &iter) == TRUE) {
+                       struct disconnect_data *disc_data;
+                       disc_data = g_malloc(sizeof(struct disconnect_data));
+                       disc_data->cb = cb;
+                       disc_data->data = user_data;
+
+                       if (mesh_gatt_notify(connection.data_out, false,
+                                               disc_notify_cb, disc_data))
+                               return;
+               }
+       }
+
+       disconnect(cb, user_data);
+}
+
+static void mesh_prov_done(void *user_data, int status);
+
+static void notify_prov_out_cb(DBusMessage *message, void *user_data)
+{
+       struct mesh_node *node = user_data;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to start notify: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       bt_shell_printf("Notify for Mesh Provisioning Out Data started\n");
+
+       if (connection.type != CONN_TYPE_PROVISION) {
+               bt_shell_printf("Error: wrong connection type %d (expected %d)\n",
+                       connection.type, CONN_TYPE_PROVISION);
+               return;
+       }
+
+       if (!connection.data_in) {
+               bt_shell_printf("Error: don't have mesh provisioning data in\n");
+               return;
+       }
+
+       if (!node) {
+               bt_shell_printf("Error: provisioning node not present\n");
+               return;
+       }
+
+       if(!prov_open(node, connection.data_in, prov_net_key_index,
+                       mesh_prov_done, node))
+       {
+               bt_shell_printf("Failed to start provisioning\n");
+               node_free(node);
+               disconnect_device(NULL, NULL);
+       } else
+               bt_shell_printf("Initiated provisioning\n");
+
+}
+
+static void session_open_cb (int status)
+{
+       if (status) {
+               bt_shell_printf("Failed to open Mesh session\n");
+               disconnect_device(NULL, NULL);
+               return;
+       }
+
+       bt_shell_printf("Mesh session is open\n");
+
+       /* Get composition data for a newly provisioned node */
+       if (connection.type == CONN_TYPE_IDENTITY)
+               config_client_get_composition(connection.unicast);
+}
+
+static void notify_proxy_out_cb(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to start notify: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       bt_shell_printf("Notify for Mesh Proxy Out Data started\n");
+
+       if (connection.type != CONN_TYPE_IDENTITY &&
+                       connection.type != CONN_TYPE_NETWORK) {
+               bt_shell_printf("Error: wrong connection type %d "
+                               "(expected %d or %d)\n", connection.type,
+                               CONN_TYPE_IDENTITY, CONN_TYPE_NETWORK);
+               return;
+       }
+
+       if (!connection.data_in) {
+               bt_shell_printf("Error: don't have mesh proxy data in\n");
+               return;
+       }
+
+       bt_shell_printf("Trying to open mesh session\n");
+       net_session_open(connection.data_in, true, session_open_cb);
+       connection.session_open = true;
+}
+
+static GDBusProxy *get_characteristic(GDBusProxy *device, const char *char_uuid)
+{
+       GList *l;
+       GDBusProxy *service;
+       const char *svc_uuid;
+
+       if (connection.type == CONN_TYPE_PROVISION) {
+               svc_uuid = MESH_PROV_SVC_UUID;
+       } else {
+               svc_uuid = MESH_PROXY_SVC_UUID;
+       }
+       for (l = service_list; l; l = l->next) {
+               if (mesh_gatt_is_child(l->data, device, "Device") &&
+                                       service_is_mesh(l->data, svc_uuid))
+                       break;
+       }
+
+       if (l)
+               service = l->data;
+       else {
+               bt_shell_printf("Mesh service not found\n");
+               return  NULL;
+       }
+
+       for (l = char_list; l; l = l->next) {
+               if (mesh_gatt_is_child(l->data, service, "Service") &&
+                                       char_is_mesh(l->data, char_uuid)) {
+                       bt_shell_printf("Found matching char: path %s, uuid %s\n",
+                               g_dbus_proxy_get_path(l->data), char_uuid);
+                       return l->data;
+               }
+       }
+       return NULL;
+}
+
+static void mesh_session_setup(GDBusProxy *proxy)
+{
+       if (connection.type == CONN_TYPE_PROVISION) {
+               connection.data_in = get_characteristic(proxy,
+                                               MESH_PROV_DATA_IN_UUID_STR);
+               if (!connection.data_in)
+                       goto fail;
+
+               connection.data_out = get_characteristic(proxy,
+                                               MESH_PROV_DATA_OUT_UUID_STR);
+               if (!connection.data_out)
+                       goto fail;
+
+               data_out_notify(connection.data_out, true, notify_prov_out_cb);
+
+       } else if (connection.type != CONN_TYPE_INVALID){
+
+               connection.data_in = get_characteristic(proxy,
+                                               MESH_PROXY_DATA_IN_UUID_STR);
+               if (!connection.data_in)
+                       goto fail;
+
+               connection.data_out = get_characteristic(proxy,
+                                               MESH_PROXY_DATA_OUT_UUID_STR);
+               if (!connection.data_out)
+                       goto fail;
+
+               data_out_notify(connection.data_out, true, notify_proxy_out_cb);
+       }
+
+       return;
+
+fail:
+
+       bt_shell_printf("Services resolved, mesh characteristics not found\n");
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+       const char *interface;
+
+       interface = g_dbus_proxy_get_interface(proxy);
+
+       if (!strcmp(interface, "org.bluez.Device1")) {
+               update_device_info(proxy);
+
+       } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+
+               adapter_added(proxy);
+
+       } else if (!strcmp(interface, "org.bluez.GattService1") &&
+                                               service_is_mesh(proxy, NULL)) {
+
+               bt_shell_printf("Service added %s\n", g_dbus_proxy_get_path(proxy));
+               service_list = g_list_append(service_list, proxy);
+
+       } else if (!strcmp(interface, "org.bluez.GattCharacteristic1") &&
+                                               char_is_mesh(proxy, NULL)) {
+
+               bt_shell_printf("Char added %s:\n", g_dbus_proxy_get_path(proxy));
+
+               char_list = g_list_append(char_list, proxy);
+       }
+}
+
+static void start_discovery_reply(DBusMessage *message, void *user_data)
+{
+       dbus_bool_t enable = GPOINTER_TO_UINT(user_data);
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to %s discovery: %s\n",
+                               enable == TRUE ? "start" : "stop", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       bt_shell_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped");
+}
+
+static struct mesh_device *find_device_by_proxy(GList *source,
+                                                       GDBusProxy *proxy)
+{
+       GList *list;
+
+       for (list = g_list_first(source); list; list = g_list_next(list)) {
+               struct mesh_device *dev = list->data;
+               GDBusProxy *proxy = dev->proxy;
+
+               if (dev->proxy == proxy)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static void device_removed(GDBusProxy *proxy)
+{
+       struct adapter *adapter = find_parent(proxy);
+       struct mesh_device *dev;
+
+       if (!adapter) {
+               /* TODO: Error */
+               return;
+       }
+
+       dev = find_device_by_proxy(adapter->mesh_devices, proxy);
+       if (dev)
+               adapter->mesh_devices = g_list_remove(adapter->mesh_devices,
+                                                                       dev);
+
+       print_device(proxy, COLORED_DEL);
+
+       if (connection.device == proxy)
+               set_connected_device(NULL);
+
+}
+
+static void adapter_removed(GDBusProxy *proxy)
+{
+       GList *ll;
+       for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) {
+               struct adapter *adapter = ll->data;
+
+               if (adapter->proxy == proxy) {
+                       print_adapter(proxy, COLORED_DEL);
+
+                       if (default_ctrl && default_ctrl->proxy == proxy) {
+                               default_ctrl = NULL;
+                               set_connected_device(NULL);
+                       }
+
+                       ctrl_list = g_list_remove_link(ctrl_list, ll);
+
+                       g_list_free_full(adapter->mesh_devices, g_free);
+                       g_free(adapter);
+                       g_list_free(ll);
+                       return;
+               }
+       }
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+       const char *interface;
+
+       interface = g_dbus_proxy_get_interface(proxy);
+
+       if (!strcmp(interface, "org.bluez.Device1")) {
+               device_removed(proxy);
+       } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+               adapter_removed(proxy);
+       } else if (!strcmp(interface, "org.bluez.GattService1")) {
+               if (proxy == connection.service) {
+                       if (service_is_mesh(proxy, MESH_PROXY_SVC_UUID)) {
+                               data_out_notify(connection.data_out,
+                                                               false, NULL);
+                               net_session_close(connection.data_in);
+                       }
+                       connection.service = NULL;
+                       connection.data_in = NULL;
+                       connection.data_out = NULL;
+               }
+
+               service_list = g_list_remove(service_list, proxy);
+
+       } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+               char_list = g_list_remove(char_list, proxy);
+       }
+}
+
+static int get_characteristic_value(DBusMessageIter *value, uint8_t *buf)
+{
+       DBusMessageIter array;
+       uint8_t *data;
+       int len;
+
+       if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+               return 0;
+
+       dbus_message_iter_recurse(value, &array);
+
+       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
+               return 0;
+
+       dbus_message_iter_get_fixed_array(&array, &data, &len);
+       memcpy(buf, data, len);
+
+       return len;
+}
+
+static bool process_mesh_characteristic(GDBusProxy *proxy)
+{
+       DBusMessageIter iter;
+       const char *uuid;
+       uint8_t *res;
+       uint8_t buf[256];
+       bool is_prov;
+
+       if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+               return false;
+
+       dbus_message_iter_get_basic(&iter, &uuid);
+
+       if (g_dbus_proxy_get_property(proxy, "Value", &iter) == FALSE)
+               return false;
+
+       is_prov = !bt_uuid_strcmp(uuid, MESH_PROV_DATA_OUT_UUID_STR);
+
+       if (is_prov || !bt_uuid_strcmp(uuid, MESH_PROXY_DATA_OUT_UUID_STR))
+       {
+               struct mesh_node *node;
+               uint16_t len;
+
+               len = get_characteristic_value(&iter, buf);
+
+               if (!len || len > 69)
+                       return false;
+
+               res = buf;
+               len = mesh_gatt_sar(&res, len);
+
+               if (!len)
+                       return false;
+
+               if (is_prov) {
+                       node = node_find_by_uuid(connection.dev_uuid);
+
+                       if (!node) {
+                               bt_shell_printf("Node not found?\n");
+                               return false;
+                       }
+
+                       return prov_data_ready(node, res, len);
+               }
+
+               return net_data_ready(res, len);
+       }
+
+       return false;
+}
+
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       const char *interface;
+
+       interface = g_dbus_proxy_get_interface(proxy);
+
+       if (!strcmp(interface, "org.bluez.Device1")) {
+
+               if (default_ctrl && device_is_child(proxy,
+                                       default_ctrl->proxy) == TRUE) {
+
+                       if (strcmp(name, "Connected") == 0) {
+                               dbus_bool_t connected;
+                               dbus_message_iter_get_basic(iter, &connected);
+
+                               if (connected && connection.device == NULL)
+                                       set_connected_device(proxy);
+                               else if (!connected &&
+                                               connection.device == proxy)
+                                       set_connected_device(NULL);
+                       } else if ((strcmp(name, "Alias") == 0) &&
+                                               connection.device == proxy) {
+                               /* Re-generate prompt */
+                               set_connected_device(proxy);
+                       } else if (!strcmp(name, "ServiceData")) {
+                               update_device_info(proxy);
+                       } else if (!strcmp(name, "ServicesResolved")) {
+                               gboolean resolved;
+
+                               dbus_message_iter_get_basic(iter, &resolved);
+
+                               bt_shell_printf("Services resolved %s\n", resolved ?
+                                                               "yes" : "no");
+
+                               if (resolved)
+                                       mesh_session_setup(connection.device);
+                       }
+
+               }
+       } else if (!strcmp(interface, "org.bluez.Adapter1")) {
+               DBusMessageIter addr_iter;
+               char *str;
+
+               bt_shell_printf("Adapter property changed \n");
+               if (g_dbus_proxy_get_property(proxy, "Address",
+                                               &addr_iter) == TRUE) {
+                       const char *address;
+
+                       dbus_message_iter_get_basic(&addr_iter, &address);
+                       str = g_strdup_printf("[" COLORED_CHG
+                                               "] Controller %s ", address);
+               } else
+                       str = g_strdup("");
+
+               if (strcmp(name, "Discovering") == 0) {
+                       int temp;
+
+                       dbus_message_iter_get_basic(iter, &temp);
+                       discovering = !!temp;
+               }
+
+               print_iter(str, name, iter);
+               g_free(str);
+       } else if (!strcmp(interface, "org.bluez.GattService1")) {
+               bt_shell_printf("Service property changed %s\n",
+                                               g_dbus_proxy_get_path(proxy));
+       } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+               bt_shell_printf("Characteristic property changed %s\n",
+                                               g_dbus_proxy_get_path(proxy));
+
+               if ((strcmp(name, "Value") == 0) &&
+                               ((connection.type == CONN_TYPE_PROVISION) ||
+                                               connection.session_open))
+                       process_mesh_characteristic(proxy);
+       }
+}
+
+static void message_handler(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
+                                       dbus_message_get_member(message));
+}
+
+static struct adapter *find_ctrl_by_address(GList *source, const char *address)
+{
+       GList *list;
+
+       for (list = g_list_first(source); list; list = g_list_next(list)) {
+               struct adapter *adapter = list->data;
+               DBusMessageIter iter;
+               const char *str;
+
+               if (g_dbus_proxy_get_property(adapter->proxy,
+                                       "Address", &iter) == FALSE)
+                       continue;
+
+               dbus_message_iter_get_basic(&iter, &str);
+
+               if (!strcmp(str, address))
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static gboolean parse_argument_on_off(int argc, char *argv[],
+                                       dbus_bool_t *value)
+{
+       if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
+               *value = TRUE;
+               return TRUE;
+       }
+
+       if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
+               *value = FALSE;
+               return TRUE;
+       }
+
+       bt_shell_printf("Invalid argument %s\n", argv[1]);
+       return FALSE;
+}
+
+static void cmd_list(int argc, char *argv[])
+{
+       GList *list;
+
+       for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
+               struct adapter *adapter = list->data;
+               print_adapter(adapter->proxy, NULL);
+       }
+}
+
+static void cmd_show(int argc, char *argv[])
+{
+       struct adapter *adapter;
+       GDBusProxy *proxy;
+       DBusMessageIter iter;
+       const char *address;
+
+
+       if (argc < 2 || !strlen(argv[1])) {
+               if (check_default_ctrl() == FALSE)
+                       return;
+
+               proxy = default_ctrl->proxy;
+       } else {
+               adapter = find_ctrl_by_address(ctrl_list, argv[1]);
+               if (!adapter) {
+                       bt_shell_printf("Controller %s not available\n",
+                                                               argv[1]);
+                       return;
+               }
+               proxy = adapter->proxy;
+       }
+
+       if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &address);
+       bt_shell_printf("Controller %s\n", address);
+
+       print_property(proxy, "Name");
+       print_property(proxy, "Alias");
+       print_property(proxy, "Class");
+       print_property(proxy, "Powered");
+       print_property(proxy, "Discoverable");
+       print_uuids(proxy);
+       print_property(proxy, "Modalias");
+       print_property(proxy, "Discovering");
+}
+
+static void cmd_select(int argc, char *argv[])
+{
+       struct adapter *adapter;
+
+       adapter = find_ctrl_by_address(ctrl_list, argv[1]);
+       if (!adapter) {
+               bt_shell_printf("Controller %s not available\n", argv[1]);
+               return;
+       }
+
+       if (default_ctrl && default_ctrl->proxy == adapter->proxy)
+               return;
+
+       forget_mesh_devices();
+
+       default_ctrl = adapter;
+       print_adapter(adapter->proxy, NULL);
+}
+
+static void generic_callback(const DBusError *error, void *user_data)
+{
+       char *str = user_data;
+
+       if (dbus_error_is_set(error))
+               bt_shell_printf("Failed to set %s: %s\n", str, error->name);
+       else
+               bt_shell_printf("Changing %s succeeded\n", str);
+}
+
+static void cmd_power(int argc, char *argv[])
+{
+       dbus_bool_t powered;
+       char *str;
+
+       if (parse_argument_on_off(argc, argv, &powered) == FALSE)
+               return;
+
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off");
+
+       if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &powered,
+                                       generic_callback, str, g_free) == TRUE)
+               return;
+
+       g_free(str);
+}
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter value;
+       char sig[2] = { type, '\0' };
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+       dbus_message_iter_append_basic(&value, type, val);
+
+       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)
+{
+       DBusMessageIter entry;
+
+       if (type == DBUS_TYPE_STRING) {
+               const char *str = *((const char **) val);
+
+               if (str == NULL)
+                       return;
+       }
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_variant(&entry, type, val);
+
+       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;
+       char **uuids;
+       size_t uuids_len;
+       dbus_bool_t duplicate;
+};
+
+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);
+
+       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);
+
+       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);
+       if (args->duplicate)
+               dict_append_entry(&dict, "DuplicateData", DBUS_TYPE_BOOLEAN,
+                                               &args->duplicate);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+
+static void set_discovery_filter_reply(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+
+       dbus_error_init(&error);
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       bt_shell_printf("SetDiscoveryFilter success\n");
+}
+
+static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;
+static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;
+static char **filtered_scan_uuids;
+static size_t filtered_scan_uuids_len;
+static char *filtered_scan_transport = "le";
+
+static void set_scan_filter_commit(void)
+{
+       struct set_discovery_filter_args args;
+
+       args.pathloss = filtered_scan_pathloss;
+       args.rssi = filtered_scan_rssi;
+       args.transport = filtered_scan_transport;
+       args.uuids = filtered_scan_uuids;
+       args.uuids_len = filtered_scan_uuids_len;
+       args.duplicate = TRUE;
+
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
+               set_discovery_filter_setup, set_discovery_filter_reply,
+               &args, NULL) == FALSE) {
+               bt_shell_printf("Failed to set discovery filter\n");
+               return;
+       }
+}
+
+static void set_scan_filter_uuids(char *filters[])
+{
+       g_strfreev(filtered_scan_uuids);
+       filtered_scan_uuids = NULL;
+       filtered_scan_uuids_len = 0;
+
+       if (!filters)
+               goto commit;
+
+       filtered_scan_uuids = g_strdupv(filters);
+       if (!filtered_scan_uuids) {
+               bt_shell_printf("Failed to parse input\n");
+               return;
+       }
+
+       filtered_scan_uuids_len = g_strv_length(filtered_scan_uuids);
+
+commit:
+       set_scan_filter_commit();
+}
+
+static void cmd_scan_unprovisioned(int argc, char *argv[])
+{
+       dbus_bool_t enable;
+       char *filters[] = { MESH_PROV_SVC_UUID, NULL };
+       const char *method;
+
+       if (parse_argument_on_off(argc, argv, &enable) == FALSE)
+               return;
+
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       if (enable == TRUE) {
+               discover_mesh = false;
+               set_scan_filter_uuids(filters);
+               method = "StartDiscovery";
+       } else {
+               method = "StopDiscovery";
+       }
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
+                               NULL, start_discovery_reply,
+                               GUINT_TO_POINTER(enable), NULL) == FALSE) {
+               bt_shell_printf("Failed to %s discovery\n",
+                                       enable == TRUE ? "start" : "stop");
+       }
+}
+
+static void cmd_info(int argc, char *argv[])
+{
+       GDBusProxy *proxy;
+       DBusMessageIter iter;
+       const char *address;
+
+       proxy = connection.device;
+       if (!proxy)
+               return;
+
+       if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
+               return;
+
+       dbus_message_iter_get_basic(&iter, &address);
+       bt_shell_printf("Device %s\n", address);
+
+       print_property(proxy, "Name");
+       print_property(proxy, "Alias");
+       print_property(proxy, "Class");
+       print_property(proxy, "Appearance");
+       print_property(proxy, "Icon");
+       print_property(proxy, "Trusted");
+       print_property(proxy, "Blocked");
+       print_property(proxy, "Connected");
+       print_uuids(proxy);
+       print_property(proxy, "Modalias");
+       print_property(proxy, "ManufacturerData");
+       print_property(proxy, "ServiceData");
+       print_property(proxy, "RSSI");
+       print_property(proxy, "TxPower");
+}
+
+static const char *security2str(uint8_t level)
+{
+       switch (level) {
+       case 0:
+               return "low";
+       case 1:
+               return "medium";
+       case 2:
+               return "high";
+       default:
+               return "invalid";
+       }
+}
+
+static void cmd_security(int argc, char *argv[])
+{
+       uint8_t level;
+       char *end;
+
+       if (argc == 1)
+               goto done;
+
+       level = strtol(argv[1], &end, 10);
+       if (end == argv[1] || !prov_set_sec_level(level)) {
+               bt_shell_printf("Invalid security level %s\n", argv[1]);
+               return;
+       }
+
+done:
+       bt_shell_printf("Provision Security Level set to %u (%s)\n",
+                       prov_get_sec_level(),
+                       security2str(prov_get_sec_level()));
+}
+
+static void cmd_connect(int argc, char *argv[])
+{
+       char *filters[] = { MESH_PROXY_SVC_UUID, NULL };
+
+       if (check_default_ctrl() == FALSE)
+               return;
+
+       memset(&connection, 0, sizeof(connection));
+
+       if (argc < 2 || !strlen(argv[1])) {
+               connection.net_idx = NET_IDX_PRIMARY;
+       } else {
+               char *end;
+               connection.net_idx = strtol(argv[1], &end, 16);
+               if (end == argv[1]) {
+                       connection.net_idx = NET_IDX_INVALID;
+                       bt_shell_printf("Invalid network index %s\n", argv[1]);
+                       return;
+               }
+
+               if (argc > 2)
+                       connection.unicast = strtol(argv[2], NULL, 16);
+       }
+
+       if (discovering)
+               g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+                                               NULL, NULL, NULL, NULL);
+
+       set_scan_filter_uuids(filters);
+       discover_mesh = true;
+
+       if (connection.unicast == UNASSIGNED_ADDRESS) {
+               connection.type = CONN_TYPE_NETWORK;
+               bt_shell_printf("Looking for mesh network with net index "
+                               "%4.4x\n", connection.net_idx);
+       } else {
+               connection.type = CONN_TYPE_IDENTITY;
+               bt_shell_printf("Looking for node id %4.4x"
+                               " on network with net index %4.4x\n",
+                               connection.unicast, connection.net_idx);
+       }
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy,
+                       "StartDiscovery", NULL, start_discovery_reply,
+                               GUINT_TO_POINTER(TRUE), NULL) == FALSE)
+               bt_shell_printf("Failed to start mesh proxy discovery\n");
+
+       g_dbus_proxy_method_call(default_ctrl->proxy, "StartDiscovery",
+                                               NULL, NULL, NULL, NULL);
+
+}
+
+static void prov_disconn_reply(DBusMessage *message, void *user_data)
+{
+       struct mesh_node *node = user_data;
+       char *filters[] = { MESH_PROXY_SVC_UUID, NULL };
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to disconnect: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       set_connected_device(NULL);
+
+       set_scan_filter_uuids(filters);
+       discover_mesh = true;
+
+       connection.type = CONN_TYPE_IDENTITY;
+       connection.data_in = NULL;
+       connection.data_out = NULL;
+       connection.unicast = node_get_primary(node);
+
+       if (g_dbus_proxy_method_call(default_ctrl->proxy,
+                       "StartDiscovery", NULL, start_discovery_reply,
+                               GUINT_TO_POINTER(TRUE), NULL) == FALSE)
+               bt_shell_printf("Failed to start mesh proxy discovery\n");
+
+}
+
+static void disconn_reply(DBusMessage *message, void *user_data)
+{
+       GDBusProxy *proxy = user_data;
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               bt_shell_printf("Failed to disconnect: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       bt_shell_printf("Successfully disconnected\n");
+
+       if (proxy != connection.device)
+               return;
+
+       set_connected_device(NULL);
+}
+
+static void cmd_disconn(int argc, char *argv[])
+{
+       if (connection.type == CONN_TYPE_PROVISION) {
+               struct mesh_node *node = node_find_by_uuid(connection.dev_uuid);
+               if (node)
+                       node_free(node);
+       }
+
+       disconnect_device(disconn_reply, connection.device);
+}
+
+static void mesh_prov_done(void *user_data, int status)
+{
+       struct mesh_node *node = user_data;
+
+       if (status){
+               bt_shell_printf("Provisioning failed\n");
+               node_free(node);
+               disconnect_device(NULL, NULL);
+               return;
+       }
+
+       bt_shell_printf("Provision success. Assigned Primary Unicast %4.4x\n",
+                                               node_get_primary(node));
+
+       if (!prov_db_add_new_node(node))
+               bt_shell_printf("Failed to add node to provisioning DB\n");
+
+       disconnect_device(prov_disconn_reply, node);
+}
+
+static void cmd_start_prov(int argc, char *argv[])
+{
+       GDBusProxy *proxy;
+       struct mesh_device *dev;
+       struct mesh_node *node;
+       int len;
+
+       len = strlen(argv[1]);
+       if ( len > 32 || len % 2) {
+               bt_shell_printf("Incorrect UUID size %d\n", len);
+       }
+
+       disconnect_device(NULL, NULL);
+
+       memset(connection.dev_uuid, 0, 16);
+       str2hex(argv[1], len, connection.dev_uuid, len/2);
+
+       node = node_find_by_uuid(connection.dev_uuid);
+       if (!node) {
+               bt_shell_printf("Device with UUID %s not found.\n", argv[1]);
+               bt_shell_printf("Stale services? Remove device and "
+                                               "re-discover\n");
+               return;
+       }
+
+       /* TODO: add command to remove a node from mesh, i.e., "unprovision" */
+       if (node_is_provisioned(node)) {
+               bt_shell_printf("Already provisioned with unicast %4.4x\n",
+                               node_get_primary(node));
+               return;
+       }
+
+       dev = find_device_by_uuid(default_ctrl->mesh_devices,
+                                 connection.dev_uuid);
+       if (!dev || !dev->proxy) {
+               bt_shell_printf("Could not find device proxy\n");
+               memset(connection.dev_uuid, 0, 16);
+               return;
+       }
+
+       proxy = dev->proxy;
+       if (discovering)
+               g_dbus_proxy_method_call(default_ctrl->proxy, "StopDiscovery",
+                                               NULL, NULL, NULL, NULL);
+       forget_mesh_devices();
+
+       connection.type = CONN_TYPE_PROVISION;
+
+       if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
+                                                       proxy, NULL) == FALSE) {
+               bt_shell_printf("Failed to connect ");
+               print_device(proxy, NULL);
+               return;
+       } else {
+               bt_shell_printf("Trying to connect ");
+               print_device(proxy, NULL);
+       }
+
+}
+
+static void cmd_print_mesh(int argc, char *argv[])
+{
+       if (!prov_db_show(mesh_prov_db_filename))
+               bt_shell_printf("Unavailable\n");
+
+}
+
+ static void cmd_print_local(int argc, char *argv[])
+{
+       if (!prov_db_show(mesh_local_config_filename))
+               bt_shell_printf("Unavailable\n");
+}
+
+static const struct bt_shell_menu main_menu = {
+       .name = "main",
+       .entries = {
+       { "list",         NULL,       cmd_list, "List available controllers"},
+       { "show",         "[ctrl]",   cmd_show, "Controller information"},
+       { "select",       "<ctrl>",   cmd_select, "Select default controller"},
+       { "security",     "[0(low)/1(medium)/2(high)]", cmd_security,
+                               "Display or change provision security level"},
+       { "info",         "[dev]",    cmd_info, "Device information"},
+       { "connect",      "[net_idx] [dst]", cmd_connect,
+                               "Connect to mesh network or node on network"},
+       { "discover-unprovisioned", "<on/off>", cmd_scan_unprovisioned,
+                                       "Look for devices to provision" },
+       { "provision",    "<uuid>",   cmd_start_prov, "Initiate provisioning"},
+       { "power",        "<on/off>", cmd_power, "Set controller power" },
+       { "disconnect",   "[dev]",    cmd_disconn, "Disconnect device"},
+       { "mesh-info",    NULL,       cmd_print_mesh,
+                                       "Mesh networkinfo (provisioner)" },
+       { "local-info",    NULL,      cmd_print_local, "Local mesh node info" },
+       { } },
+};
+
+static const char *mesh_config_dir;
+
+static const struct option options[] = {
+       { "config",     required_argument, 0, 'c' },
+       { 0, 0, 0, 0 }
+};
+
+static const char **optargs[] = {
+       &mesh_config_dir
+};
+
+static const char *help[] = {
+       "Read local mesh config JSON files from <directory>"
+};
+
+static const struct bt_shell_opt opt = {
+       .options = options,
+       .optno = sizeof(options) / sizeof(struct option),
+       .optstr = "c:",
+       .optarg = optargs,
+       .help = help,
+};
+
+static void client_ready(GDBusClient *client, void *user_data)
+{
+       bt_shell_attach(fileno(stdin));
+}
+
+int main(int argc, char *argv[])
+{
+       GDBusClient *client;
+       int len;
+       int extra;
+
+       bt_shell_init(argc, argv, &opt);
+       bt_shell_set_menu(&main_menu);
+       bt_shell_set_prompt(PROMPT_OFF);
+
+       if (!mesh_config_dir) {
+               bt_shell_printf("Local config directory not provided.\n");
+               mesh_config_dir = "";
+       } else {
+               bt_shell_printf("Reading prov_db.json and local_node.json from %s\n",
+                                                       mesh_config_dir);
+       }
+
+       len = strlen(mesh_config_dir);
+       if (len && mesh_config_dir[len - 1] != '/') {
+               extra = 1;
+               bt_shell_printf("mesh_config_dir[%d] %s\n", len,
+                                               &mesh_config_dir[len - 1]);
+       } else {
+               extra = 0;
+       }
+       mesh_local_config_filename = g_malloc(len + strlen("local_node.json")
+                                                                       + 2);
+       if (!mesh_local_config_filename)
+               exit(1);
+
+       mesh_prov_db_filename = g_malloc(len + strlen("prov_db.json") + 2);
+       if (!mesh_prov_db_filename) {
+               exit(1);
+       }
+
+       sprintf(mesh_local_config_filename, "%s", mesh_config_dir);
+
+       if (extra)
+               sprintf(mesh_local_config_filename + len , "%c", '/');
+
+       sprintf(mesh_local_config_filename + len + extra, "%s",
+                                                       "local_node.json");
+       len = len + extra + strlen("local_node.json");
+       sprintf(mesh_local_config_filename + len, "%c", '\0');
+
+       if (!prov_db_read_local_node(mesh_local_config_filename, true)) {
+               g_printerr("Failed to parse local node configuration file %s\n",
+                       mesh_local_config_filename);
+               exit(1);
+       }
+
+       sprintf(mesh_prov_db_filename, "%s", mesh_config_dir);
+       len = strlen(mesh_config_dir);
+       if (extra)
+               sprintf(mesh_prov_db_filename + len , "%c", '/');
+
+       sprintf(mesh_prov_db_filename + len + extra, "%s", "prov_db.json");
+       sprintf(mesh_prov_db_filename + len + extra + strlen("prov_db.json"),
+                                                               "%c", '\0');
+
+       if (!prov_db_read(mesh_prov_db_filename)) {
+               g_printerr("Failed to parse provisioning database file %s\n",
+                       mesh_prov_db_filename);
+               exit(1);
+       }
+
+       dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+       client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
+
+       g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+       g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
+       g_dbus_client_set_signal_watch(client, message_handler, NULL);
+
+       g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+                                                       property_changed, NULL);
+
+       g_dbus_client_set_ready_watch(client, client_ready, NULL);
+
+       if (!config_client_init())
+               g_printerr("Failed to initialize mesh configuration client\n");
+
+       if (!config_server_init())
+               g_printerr("Failed to initialize mesh configuration server\n");
+
+       if (!onoff_client_init(PRIMARY_ELEMENT_IDX))
+               g_printerr("Failed to initialize mesh generic On/Off client\n");
+
+       bt_shell_run();
+
+       g_dbus_client_unref(client);
+
+       dbus_connection_unref(dbus_conn);
+
+       node_cleanup();
+
+       g_list_free(char_list);
+       g_list_free(service_list);
+       g_list_free_full(ctrl_list, proxy_leak);
+
+       return 0;
+}
diff --git a/mesh/mesh-net.h b/mesh/mesh-net.h
new file mode 100644 (file)
index 0000000..76a9822
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+/* Proxy PDU Types */
+#define PROXY_NETWORK_PDU      0x00
+#define PROXY_MESH_BEACON      0x01
+#define PROXY_CONFIG_PDU       0x02
+#define PROXY_PROVISIONING_PDU 0x03
+
+#define CTL            0x80
+#define TTL_MASK       0x7f
+#define SEQ_MASK       0xffffff
+
+#define CREDFLAG_MASK  0x1000
+#define APP_IDX_MASK   0x0fff
+#define APP_IDX_DEV    0x7fff
+#define APP_IDX_ANY    0x8000
+#define APP_IDX_NET    0xffff
+#define APP_IDX_INVALID        0xffff
+
+#define NET_IDX_INVALID        0xffff
+#define NET_IDX_PRIMARY        0x0000
+
+#define KEY_CACHE_SIZE 64
+#define FRND_CACHE_MAX 32
+
+#define UNASSIGNED_ADDRESS     0x0000
+#define PROXIES_ADDRESS                0xfffc
+#define FRIENDS_ADDRESS                0xfffd
+#define RELAYS_ADDRESS         0xfffe
+#define ALL_NODES_ADDRESS      0xffff
+#define VIRTUAL_ADDRESS_LOW    0x8000
+#define VIRTUAL_ADDRESS_HIGH   0xbfff
+#define GROUP_ADDRESS_LOW      0xc000
+#define GROUP_ADDRESS_HIGH     0xff00
+
+#define DEFAULT_TTL            0xff
+
+#define PRIMARY_ELEMENT_IDX    0x00
+
+#define MAX_UNSEG_LEN  15 /* msg_len == 11 + sizeof(MIC) */
+#define MAX_SEG_LEN    12 /* UnSeg length - 3 octets overhead */
+#define SEG_MAX(len)   (((len) <= MAX_UNSEG_LEN) ? 0 : \
+               (((len) - 1) / MAX_SEG_LEN))
+#define SEG_OFF(seg)   ((seg) * MAX_SEG_LEN)
+#define MAX_SEG_TO_LEN(seg)    ((seg) ? SEG_OFF((seg) + 1) : MAX_UNSEG_LEN)
+
+
+#define IS_UNASSIGNED(x)       ((x) == UNASSIGNED_ADDRESS)
+#define IS_UNICAST(x)          (((x) > UNASSIGNED_ADDRESS) && \
+                                       ((x) < VIRTUAL_ADDRESS_LOW))
+#define IS_VIRTUAL(x)          (((x) >= VIRTUAL_ADDRESS_LOW) && \
+                                       ((x) <= VIRTUAL_ADDRESS_HIGH))
+#define IS_GROUP(x)            (((x) >= GROUP_ADDRESS_LOW) && \
+                                       ((x) <= GROUP_ADDRESS_HIGH))
+#define IS_ALL_NODES(x)                ((x) == ALL_NODES_ADDRESS)
+
+#define SEGMENTED      0x80
+#define UNSEGMENTED    0x00
+#define SEG_HDR_SHIFT  31
+#define IS_SEGMENTED(hdr)      (!!((hdr) & (true << SEG_HDR_SHIFT)))
+
+#define KEY_ID_MASK    0x7f
+#define KEY_AID_MASK   0x3f
+#define KEY_ID_AKF     0x40
+#define KEY_AID_SHIFT  0
+#define AKF_HDR_SHIFT  30
+#define KEY_HDR_SHIFT  24
+#define HAS_APP_KEY(hdr)       (!!((hdr) & (true << AKF_HDR_SHIFT)))
+
+#define OPCODE_MASK    0x7f
+#define OPCODE_HDR_SHIFT       24
+#define RELAY          0x80
+#define RELAY_HDR_SHIFT        23
+#define SZMIC          0x80
+#define SZMIC_HDR_SHIFT        23
+#define SEQ_ZERO_MASK  0x1fff
+#define SEQ_ZERO_HDR_SHIFT     10
+#define IS_RELAYED(hdr)        (!!((hdr) & (true << RELAY_HDR_SHIFT)))
+#define HAS_MIC64(hdr) (!!((hdr) & (true << SZMIC_HDR_SHIFT)))
+
+#define SEG_MASK       0x1f
+#define SEGO_HDR_SHIFT 5
+#define SEGN_HDR_SHIFT 0
+#define SEG_TOTAL(hdr) (((hdr) >> SEGN_HDR_SHIFT) & SEG_MASK)
+/* Proxy Configuration Opcodes */
+#define PROXY_OP_SET_FILTER_TYPE       0x00
+#define PROXY_OP_FILTER_ADD            0x01
+#define PROXY_OP_FILTER_DEL            0x02
+#define PROXY_OP_FILTER_STATUS         0x03
+
+/* Proxy Filter Defines */
+#define PROXY_FILTER_WHITELIST         0x00
+#define PROXY_FILTER_BLACKLIST         0x01
+
+/* Network Tranport Opcodes */
+#define NET_OP_SEG_ACKNOWLEDGE         0x00
+#define NET_OP_FRND_POLL               0x01
+#define NET_OP_FRND_UPDATE             0x02
+#define NET_OP_FRND_REQUEST            0x03
+#define NET_OP_FRND_OFFER              0x04
+#define NET_OP_FRND_CLEAR              0x05
+#define NET_OP_FRND_CLEAR_CONFIRM      0x06
+
+#define NET_OP_PROXY_SUB_ADD           0x07
+#define NET_OP_PROXY_SUB_REMOVE                0x08
+#define NET_OP_PROXY_SUB_CONFIRM       0x09
+#define NET_OP_HEARTBEAT               0x0a
+
+/* Key refresh state on the mesh */
+#define NET_KEY_REFRESH_PHASE_NONE     0x00
+#define NET_KEY_REFRESH_PHASE_ONE      0x01
+#define NET_KEY_REFRESH_PHASE_TWO      0x02
+#define NET_KEY_REFRESH_PHASE_THREE    0x03
+
+#define MESH_FEATURE_RELAY     1
+#define MESH_FEATURE_PROXY     2
+#define MESH_FEATURE_FRIEND    4
+#define MESH_FEATURE_LPN       8
+
+#define MESH_MAX_ACCESS_PAYLOAD                380
+
+#define MESH_STATUS_SUCCESS            0x00
+#define MESH_STATUS_INVALID_ADDRESS    0x01
+#define MESH_STATUS_INVALID_MODEL      0x02
+#define MESH_STATUS_INVALID_APPKEY     0x03
+#define MESH_STATUS_INVALID_NETKEY     0x04
+#define MESH_STATUS_INSUFF_RESOURCES   0x05
+#define MESH_STATUS_IDX_ALREADY_STORED 0x06
+#define MESH_STATUS_INVALID_PUB_PARAM  0x07
+#define MESH_STATUS_NOT_SUB_MOD                0x08
+#define MESH_STATUS_STORAGE_FAIL       0x09
+#define MESH_STATUS_FEAT_NOT_SUP       0x0a
+#define MESH_STATUS_CANNOT_UPDATE      0x0b
+#define MESH_STATUS_CANNOT_REMOVE      0x0c
+#define MESH_STATUS_CANNOT_BIND                0x0d
+#define MESH_STATUS_UNABLE_CHANGE_STATE        0x0e
+#define MESH_STATUS_CANNOT_SET         0x0f
+#define MESH_STATUS_UNSPECIFIED_ERROR  0x10
+#define MESH_STATUS_INVALID_BINDING    0x11
diff --git a/mesh/net.c b/mesh/net.c
new file mode 100644 (file)
index 0000000..20dfcb8
--- /dev/null
@@ -0,0 +1,2202 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+
+#include "mesh/crypto.h"
+#include "mesh/gatt.h"
+#include "mesh/mesh-net.h"
+#include "mesh/util.h"
+#include "mesh/keys.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/net.h"
+
+struct address_range
+{
+       uint16_t min;
+       uint16_t max;
+};
+
+struct mesh_net {
+       uint32_t iv_index;
+       uint32_t seq_num;
+       uint32_t seq_num_reserved;
+       uint16_t primary_addr;
+       uint8_t iv_upd_state;
+       uint8_t num_elements;
+       uint8_t default_ttl;
+       bool iv_update;
+       bool provisioner;
+       bool blacklist;
+       guint iv_update_timeout;
+       GDBusProxy *proxy_in;
+       GList *address_pool;
+       GList *dest;    /* List of valid local destinations for Whitelist */
+       GList *sar_in;  /* Incoming segmented messages in progress */
+       GList *msg_out; /* Pre-Network encoded, might be multi-segment */
+       GList *pkt_out; /* Fully encoded packets awaiting Tx in order */
+       net_mesh_session_open_callback open_cb;
+};
+
+struct generic_key {
+       uint16_t        idx;
+};
+
+struct net_key_parts {
+       uint8_t nid;
+       uint8_t enc_key[16];
+       uint8_t privacy_key[16];
+       uint8_t net_key[16];
+       uint8_t beacon_key[16];
+       uint8_t net_id[8];
+};
+
+struct mesh_net_key {
+       struct generic_key      generic;
+       uint8_t                 phase;
+       struct net_key_parts    current;
+       struct net_key_parts    new;
+};
+
+struct app_key_parts {
+       uint8_t key[16];
+       uint8_t akf_aid;
+};
+
+struct mesh_app_key {
+       struct generic_key      generic;
+       uint16_t                net_idx;
+       struct app_key_parts    current;
+       struct app_key_parts    new;
+};
+
+struct mesh_virt_addr {
+       uint16_t        va16;
+       uint32_t        va32;
+       uint8_t         va128[16];
+};
+
+struct mesh_pkt {
+       uint8_t         data[30];
+       uint8_t         len;
+};
+
+struct mesh_sar_msg {
+       guint           ack_to;
+       guint           msg_to;
+       uint32_t        iv_index;
+       uint32_t        seqAuth;
+       uint32_t        ack;
+       uint32_t        dst;
+       uint16_t        src;
+       uint16_t        net_idx;
+       uint16_t        len;
+       uint8_t         akf_aid;
+       uint8_t         ttl;
+       uint8_t         segN;
+       uint8_t         activity_cnt;
+       bool            ctl;
+       bool            segmented;
+       bool            szmic;
+       bool            proxy;
+       uint8_t         data[20]; /* Open ended, min 20 */
+};
+
+struct mesh_destination {
+       uint16_t        cnt;
+       uint16_t        dst;
+};
+
+/* Network Packet Layer based Offsets */
+#define AKF_BIT                        0x40
+
+#define PKT_IVI(p)             !!((p)[0] & 0x80)
+#define SET_PKT_IVI(p,v)       do {(p)[0] &= 0x7f; \
+                                       (p)[0] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_NID(p)             ((p)[0] & 0x7f)
+#define SET_PKT_NID(p,v)       do {(p)[0] &= 0x80; (p)[0] |= (v);} while(0)
+#define PKT_CTL(p)             (!!((p)[1] & 0x80))
+#define SET_PKT_CTL(p,v)       do {(p)[1] &= 0x7f; \
+                                       (p)[1] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_TTL(p)             ((p)[1] & 0x7f)
+#define SET_PKT_TTL(p,v)       do {(p)[1] &= 0x80; (p)[1] |= (v);} while(0)
+#define PKT_SEQ(p)             (get_be32((p) + 1) & 0xffffff)
+#define SET_PKT_SEQ(p,v)       put_be32(((p)[1] << 24) + ((v) & 0xffffff), \
+                                                                       (p) + 1)
+#define PKT_SRC(p)             get_be16((p) + 5)
+#define SET_PKT_SRC(p,v)       put_be16(v, (p) + 5)
+#define PKT_DST(p)             get_be16((p) + 7)
+#define SET_PKT_DST(p,v)       put_be16(v, (p) + 7)
+#define PKT_TRANS(p)           ((p) + 9)
+#define PKT_TRANS_LEN(l)       ((l) - 9)
+
+#define PKT_SEGMENTED(p)       (!!((p)[9] & 0x80))
+#define SET_PKT_SEGMENTED(p,v) do {(p)[9] &= 0x7f; \
+                                       (p)[9] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_AKF_AID(p)         ((p)[9] & 0x7f)
+#define SET_PKT_AKF_AID(p,v)   do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
+#define PKT_OPCODE(p)          ((p)[9] & 0x7f)
+#define SET_PKT_OPCODE(p,v)    do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
+#define PKT_OBO(p)             (!!((p)[10] & 0x80))
+#define PKT_SZMIC(p)           (!!(PKT_SEGMENTED(p) ? ((p)[10] & 0x40) : 0))
+#define SET_PKT_SZMIC(p,v)     do {(p)[10] &= 0x7f; \
+                                       (p)[10] |= ((v) ? 0x80 : 0);} while(0)
+#define PKT_SEQ0(p)            ((get_be16((p) + 10) >> 2) & 0x1fff)
+#define SET_PKT_SEQ0(p,v)      do {put_be16((get_be16((p) + 10) & 0x8003) \
+                                       | (((v) & 0x1fff) << 2), \
+                                       (p) + 10);} while(0)
+#define SET_PKT_SEGO(p,v)      do {put_be16((get_be16( \
+                                       (p) + 11) & 0xfc1f) | ((v) << 5), \
+                                       (p) + 11);} while(0)
+#define SET_PKT_SEGN(p,v)      do {(p)[12] = ((p)[12] & 0xe0) | (v);} while(0)
+#define PKT_ACK(p)             (get_be32((p) + 12))
+#define SET_PKT_ACK(p,v)       (put_be32((v)(p) + 12))
+
+/* Transport Layer based offsets */
+#define TRANS_SEGMENTED(t)     (!!((t)[0] & 0x80))
+#define SET_TRANS_SEGMENTD(t,v)        do {(t)[0] &= 0x7f; \
+                                       (t)[0] |= ((v) ? 0x80 : 0);} while(0)
+#define TRANS_OPCODE(t)                ((t)[0] & 0x7f)
+#define SET_TRANS_OPCODE(t,v)  do {(t)[0] &= 0x80; (t)[0] |= (v);} while(0)
+#define TRANS_AKF_AID(t)               ((t)[0] & 0x7f)
+#define SET_TRANS_AKF_AID(t,v) do {(t)[0] &= 0xc0; (t)[0] |= (v);} while(0)
+#define TRANS_AKF(t)           (!!((t)[0] & AKF_BIT))
+#define TRANS_SZMIC(t)         (!!(TRANS_SEGMENTED(t) ? ((t)[1] & 0x80) : 0))
+#define TRANS_SEQ0(t)          ((get_be16((t) + 1) >> 2) & 0x1fff)
+#define SET_TRANS_SEQ0(t,v)    do {put_be16((get_be16((t) + 1) & 0x8003) \
+                                       | (((v) & 0x1fff) << 2), \
+                                       (t) + 1);} while(0)
+#define SET_TRANS_ACK(t,v)     put_be32((v), (t) + 3)
+#define TRANS_SEGO(t)          ((get_be16((t) + 2) >> 5) & 0x1f)
+#define TRANS_SEGN(t)          ((t)[3] & 0x1f)
+
+#define TRANS_PAYLOAD(t)       ((t) + (TRANS_SEGMENTED(t) ? 4 : 1))
+#define TRANS_LEN(t,l)         ((l) -(TRANS_SEGMENTED(t) ? 4 : 1))
+
+/* Proxy Config Opcodes */
+#define FILTER_SETUP           0x00
+#define FILTER_ADD             0x01
+#define FILTER_DEL             0x02
+#define FILTER_STATUS          0x03
+
+/* Proxy Filter Types */
+#define WHITELIST_FILTER       0x00
+#define BLACKLIST_FILTER       0x01
+
+/* IV Updating states for timing enforcement */
+#define IV_UPD_INIT            0
+#define IV_UPD_NORMAL          1
+#define IV_UPD_UPDATING                2
+#define IV_UPD_NORMAL_HOLD     3
+
+#define IV_IDX_DIFF_RANGE      42
+
+static struct mesh_net net;
+static GList *virt_addrs = NULL;
+static GList *net_keys = NULL;
+static GList *app_keys = NULL;
+
+/* Forward static declarations */
+static void resend_segs(struct mesh_sar_msg *sar);
+
+static int match_net_id(const void *a, const void *net_id)
+{
+       const struct mesh_net_key *net_key = a;
+
+       if (net_key->current.nid != 0xff &&
+                       !memcmp(net_key->current.net_id, net_id, 8))
+               return 0;
+
+       if (net_key->new.nid != 0xff &&
+                       !memcmp(net_key->new.net_id, net_id, 8))
+               return 0;
+
+       return -1;
+}
+
+static struct mesh_net_key *find_net_key_by_id(const uint8_t *net_id)
+{
+       GList *l;
+
+       l = g_list_find_custom(net_keys, net_id, match_net_id);
+
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon)
+{
+       struct mesh_net_key *net_key = find_net_key_by_id(proxy_beacon);
+
+       if (net_key == NULL)
+               return NET_IDX_INVALID;
+
+       return net_key->generic.idx;
+}
+
+static int match_sar_dst(const void *a, const void *b)
+{
+       const struct mesh_sar_msg *sar = a;
+       uint16_t dst = GPOINTER_TO_UINT(b);
+
+       return (sar->dst == dst) ? 0 : -1;
+}
+
+static struct mesh_sar_msg *find_sar_out_by_dst(uint16_t dst)
+{
+       GList *l;
+
+       l = g_list_find_custom(net.msg_out, GUINT_TO_POINTER(dst),
+                       match_sar_dst);
+
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int match_sar_src(const void *a, const void *b)
+{
+       const struct mesh_sar_msg *sar = a;
+       uint16_t src = GPOINTER_TO_UINT(b);
+
+       return (sar->src == src) ? 0 : -1;
+}
+
+static struct mesh_sar_msg *find_sar_in_by_src(uint16_t src)
+{
+       GList *l;
+
+       l = g_list_find_custom(net.sar_in, GUINT_TO_POINTER(src),
+                       match_sar_src);
+
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int match_key_index(const void *a, const void *b)
+{
+       const struct generic_key *generic = a;
+       uint16_t index = GPOINTER_TO_UINT(b);
+
+       return (generic->idx == index) ? 0 : -1;
+}
+
+static bool delete_key(GList **list, uint16_t index)
+{
+       GList *l;
+
+       l = g_list_find_custom(*list, GUINT_TO_POINTER(index),
+                               match_key_index);
+
+       if (!l)
+               return false;
+
+       *list = g_list_delete_link(*list, l);
+
+       return true;
+
+}
+
+static uint8_t *get_key(GList *list, uint16_t index)
+{
+       GList *l;
+       struct mesh_app_key *app_key;
+       struct mesh_net_key *net_key;
+
+       l = g_list_find_custom(list, GUINT_TO_POINTER(index),
+                               match_key_index);
+
+       if (!l) return NULL;
+
+       if (list == app_keys) {
+               app_key = l->data;
+
+               /* All App Keys must belong to a valid Net Key */
+               l = g_list_find_custom(net_keys,
+                               GUINT_TO_POINTER(app_key->net_idx),
+                               match_key_index);
+
+               if (!l) return NULL;
+
+               net_key = l->data;
+
+               if (net_key->phase == 2 && app_key->new.akf_aid != 0xff)
+                       return app_key->new.key;
+
+               if (app_key->current.akf_aid != 0xff)
+                       return app_key->current.key;
+
+               return NULL;
+       }
+
+       net_key = l->data;
+
+       if (net_key->phase == 2 && net_key->new.nid != 0xff)
+               return net_key->new.net_key;
+
+       if (net_key->current.nid != 0xff)
+               return net_key->current.net_key;
+
+       return NULL;
+}
+
+bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
+                       bool update)
+{
+       struct mesh_app_key *app_key = NULL;
+       uint8_t akf_aid;
+       GList *l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+                               match_key_index);
+
+       if (!mesh_crypto_k4(key, &akf_aid))
+               return false;
+
+       akf_aid |= AKF_BIT;
+
+       if (l && update) {
+
+               app_key = l->data;
+
+               if (app_key->net_idx != net_idx)
+                       return false;
+
+               memcpy(app_key->new.key, key, 16);
+               app_key->new.akf_aid = akf_aid;
+
+       } else if (l) {
+
+               app_key = l->data;
+
+               if (memcmp(app_key->current.key, key, 16) ||
+                               app_key->net_idx != net_idx)
+                       return false;
+
+       } else {
+
+               app_key = g_new(struct mesh_app_key, 1);
+               memcpy(app_key->current.key, key, 16);
+               app_key->net_idx = net_idx;
+               app_key->generic.idx = app_idx;
+               app_key->current.akf_aid = akf_aid;
+
+               /* Invalidate "New" version */
+               app_key->new.akf_aid = 0xff;
+
+               app_keys = g_list_append(app_keys, app_key);
+
+       }
+
+       return true;
+}
+
+bool keys_net_key_add(uint16_t net_idx, uint8_t *key, bool update)
+{
+       struct mesh_net_key *net_key = NULL;
+       uint8_t p = 0;
+       GList *l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+                               match_key_index);
+
+       if (l && update) {
+               bool result;
+
+               net_key = l->data;
+
+               memcpy(net_key->new.net_key, key, 16);
+
+               /* Calculate the many component parts */
+               result = mesh_crypto_nkbk(key, net_key->new.beacon_key);
+               if (!result)
+                       return false;
+
+               result = mesh_crypto_k3(key, net_key->new.net_id);
+               if (!result)
+                       return false;
+
+               result = mesh_crypto_k2(key, &p, 1,
+                               &net_key->new.nid,
+                               net_key->new.enc_key,
+                               net_key->new.privacy_key);
+               if (!result)
+                       net_key->new.nid = 0xff;
+
+               return result;
+
+       } else if (l) {
+               net_key = l->data;
+
+               if (memcmp(net_key->current.net_key, key, 16))
+                       return false;
+       } else {
+               bool result;
+
+               net_key = g_new(struct mesh_net_key, 1);
+               memcpy(net_key->current.net_key, key, 16);
+               net_key->generic.idx = net_idx;
+
+               /* Invalidate "New" version */
+               net_key->new.nid = 0xff;
+
+               /* Calculate the many component parts */
+               result = mesh_crypto_nkbk(key, net_key->current.beacon_key);
+               if (!result) {
+                       g_free(net_key);
+                       return false;
+               }
+
+               result = mesh_crypto_k3(key, net_key->current.net_id);
+               if (!result) {
+                       g_free(net_key);
+                       return false;
+               }
+
+               result = mesh_crypto_k2(key, &p, 1,
+                               &net_key->current.nid,
+                               net_key->current.enc_key,
+                               net_key->current.privacy_key);
+
+               if (!result) {
+                       g_free(net_key);
+                       return false;
+               }
+
+               net_keys = g_list_append(net_keys, net_key);
+       }
+
+       return true;
+}
+
+static struct mesh_app_key *find_app_key_by_idx(uint16_t app_idx)
+{
+       GList *l;
+
+       l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+                               match_key_index);
+
+       if (!l) return NULL;
+
+       return l->data;
+}
+
+static struct mesh_net_key *find_net_key_by_idx(uint16_t net_idx)
+{
+       GList *l;
+
+       l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+                               match_key_index);
+
+       if (!l) return NULL;
+
+       return l->data;
+}
+
+static int match_virt_dst(const void *a, const void *b)
+{
+       const struct mesh_virt_addr *virt = a;
+       uint32_t dst = GPOINTER_TO_UINT(b);
+
+       if (dst < 0x10000 && dst == virt->va16)
+               return 0;
+
+       if (dst == virt->va32)
+               return 0;
+
+       return -1;
+}
+
+static struct mesh_virt_addr *find_virt_by_dst(uint32_t dst)
+{
+       GList *l;
+
+       l = g_list_find_custom(virt_addrs, GUINT_TO_POINTER(dst),
+                               match_virt_dst);
+
+       if (!l) return NULL;
+
+       return l->data;
+}
+
+uint8_t *keys_net_key_get(uint16_t net_idx, bool current)
+{
+       GList *l;
+
+
+       l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+                               match_key_index);
+       if (!l) {
+               return NULL;
+       } else {
+               struct mesh_net_key *key = l->data;
+               if (current)
+                       return key->current.net_key;
+               else
+                       return key->new.net_key;
+       }
+}
+
+bool keys_app_key_delete(uint16_t app_idx)
+{
+       /* TODO: remove all associated bindings */
+       return delete_key(&app_keys, app_idx);
+}
+
+bool keys_net_key_delete(uint16_t net_idx)
+{
+       /* TODO: remove all associated app keys and bindings */
+       return delete_key(&net_keys, net_idx);
+}
+
+uint8_t keys_get_kr_phase(uint16_t net_idx)
+{
+       GList *l;
+       struct mesh_net_key *key;
+
+       l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
+                               match_key_index);
+
+       if (!l)
+               return KR_PHASE_INVALID;
+
+       key = l->data;
+
+       return key->phase;
+}
+
+bool keys_set_kr_phase(uint16_t index, uint8_t phase)
+{
+       GList *l;
+       struct mesh_net_key *net_key;
+
+       l = g_list_find_custom(net_keys, GUINT_TO_POINTER(index),
+                               match_key_index);
+
+       if (!l)
+               return false;
+
+       net_key = l->data;
+       net_key->phase = phase;
+
+       return true;
+}
+
+uint16_t keys_app_key_get_bound(uint16_t app_idx)
+{
+       GList *l;
+
+       l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+                               match_key_index);
+       if (!l)
+               return NET_IDX_INVALID;
+       else {
+               struct mesh_app_key *key = l->data;
+               return key->net_idx;
+       }
+}
+
+uint8_t *keys_app_key_get(uint16_t app_idx, bool current)
+{
+       GList *l;
+
+
+       l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
+                               match_key_index);
+       if (!l) {
+               return NULL;
+       } else {
+               struct mesh_app_key *key = l->data;
+               if (current)
+                       return key->current.key;
+               else
+                       return key->new.key;
+       }
+}
+
+void keys_cleanup_all(void)
+{
+       g_list_free_full(app_keys, g_free);
+       g_list_free_full(net_keys, g_free);
+       app_keys = net_keys = NULL;
+}
+
+bool net_get_key(uint16_t net_idx, uint8_t *key)
+{
+       uint8_t *buf;
+
+       buf = get_key(net_keys, net_idx);
+
+       if (!buf)
+               return false;
+
+       memcpy(key, buf, 16);
+       return true;
+}
+
+bool net_get_flags(uint16_t net_idx, uint8_t *out_flags)
+{
+       uint8_t phase;
+
+       phase = keys_get_kr_phase(net_idx);
+
+       if (phase == KR_PHASE_INVALID || !out_flags)
+               return false;
+
+       if (phase != KR_PHASE_NONE)
+               *out_flags = 0x01;
+       else
+               *out_flags = 0x00;
+
+       if (net.iv_update)
+               *out_flags |= 0x02;
+
+       return true;
+}
+
+uint32_t net_get_iv_index(bool *update)
+{
+       if (update)
+               *update = net.iv_update;
+
+       return net.iv_index;
+}
+
+void net_set_iv_index(uint32_t iv_index, bool update)
+{
+       net.iv_index = iv_index;
+       net.iv_update = update;
+}
+
+void set_sequence_number(uint32_t seq_num)
+{
+       net.seq_num = seq_num;
+}
+
+uint32_t get_sequence_number(void)
+{
+       return net.seq_num;
+}
+
+bool net_add_address_pool(uint16_t min, uint16_t max)
+{
+       uint32_t range;
+       if (max < min)
+               return false;
+       range = min + (max << 16);
+       net.address_pool = g_list_append(net.address_pool,
+                                               GUINT_TO_POINTER(range));
+       return true;
+}
+
+static int match_address_range(const void *a, const void *b)
+{
+       uint32_t range = GPOINTER_TO_UINT(a);
+       uint8_t num_elements = (uint8_t) (GPOINTER_TO_UINT(b));
+       uint16_t max = range >> 16;
+       uint16_t min = range & 0xffff;
+
+       return ((max - min) >= (num_elements - 1)) ? 0 : -1;
+
+}
+
+uint16_t net_obtain_address(uint8_t num_eles)
+{
+       uint16_t addr;
+       GList *l;
+
+       l = g_list_find_custom(net.address_pool, GUINT_TO_POINTER(num_eles),
+                               match_address_range);
+       if (l) {
+               uint32_t range = GPOINTER_TO_UINT(l->data);
+               uint16_t max = range >> 16;
+               uint16_t min = range & 0xffff;
+
+               addr = min;
+               min += num_eles;
+
+               if (min > max)
+                       net.address_pool = g_list_delete_link(net.address_pool,
+                                                               l);
+               else {
+                       range = min + (max << 16);
+                       l->data = GUINT_TO_POINTER(range);
+               }
+               return addr;
+       }
+
+       return UNASSIGNED_ADDRESS;
+}
+
+static int range_cmp(const void *a, const void *b)
+{
+       uint32_t range1 = GPOINTER_TO_UINT(a);
+       uint32_t range2 = GPOINTER_TO_UINT(b);
+
+       return range2 - range1;
+}
+
+void net_release_address(uint16_t addr, uint8_t num_elements)
+{
+       GList *l;
+       uint32_t range;
+
+       for (l = net.address_pool; l != NULL; l = l->next)
+       {
+               uint16_t max;
+               uint16_t min;
+
+               range = GPOINTER_TO_UINT(l->data);
+
+               max = range >> 16;
+               min = range & 0xffff;
+
+               if (min == (addr + num_elements + 1))
+                       min  = addr;
+               else if (addr && max == (addr - 1))
+                       max = addr + num_elements + 1;
+               else
+                       continue;
+
+               range = min + (max << 16);
+               l->data = GUINT_TO_POINTER(range);
+               return;
+       }
+
+       range = addr + ((addr + num_elements - 1) << 16);
+       net.address_pool = g_list_insert_sorted(net.address_pool,
+                                               GUINT_TO_POINTER(range),
+                                               range_cmp);
+}
+
+bool net_reserve_address_range(uint16_t base, uint8_t num_elements)
+{
+       GList *l;
+       uint32_t range;
+       uint16_t max;
+       uint16_t min;
+       bool shrink;
+
+       for (l = net.address_pool; l != NULL; l = l->next) {
+
+               range = GPOINTER_TO_UINT(l->data);
+
+               max = range >> 16;
+               min = range & 0xffff;
+
+               if (base >= min && (base + num_elements - 1) <= max)
+                       break;
+       }
+
+       if (!l)
+               return false;
+
+       net.address_pool = g_list_delete_link(net.address_pool, l);
+
+       shrink = false;
+
+       if (base == min) {
+               shrink = true;
+               min = base + num_elements;
+       }
+
+       if (max == base + num_elements - 1) {
+               shrink = true;
+               max -= num_elements;
+       }
+
+       if (min > max)
+               return true;
+
+       if (shrink)
+               range = min + (max << 16);
+       else
+               range = min + ((base - 1) << 16);
+
+       net.address_pool = g_list_insert_sorted(net.address_pool,
+                                               GUINT_TO_POINTER(range),
+                                               range_cmp);
+
+       if (shrink)
+               return true;
+
+       range = (base + num_elements) + (max << 16);
+       net.address_pool = g_list_insert_sorted(net.address_pool,
+                                               GUINT_TO_POINTER(range),
+                                               range_cmp);
+
+       return true;
+}
+
+static int match_destination(const void *a, const void *b)
+{
+       const struct mesh_destination *dest = a;
+       uint16_t dst = GPOINTER_TO_UINT(b);
+
+       return (dest->dst == dst) ? 0 : -1;
+}
+
+void net_dest_ref(uint16_t dst)
+{
+       struct mesh_destination *dest;
+       GList *l;
+
+       if (!dst) return;
+
+       l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
+                       match_destination);
+
+       if (l) {
+               dest = l->data;
+               dest->cnt++;
+               return;
+       }
+
+       dest = g_new0(struct mesh_destination, 1);
+       dest->dst = dst;
+       dest->cnt++;
+       net.dest = g_list_append(net.dest, dest);
+}
+
+void net_dest_unref(uint16_t dst)
+{
+       struct mesh_destination *dest;
+       GList *l;
+
+       l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
+                       match_destination);
+
+       if (!l)
+               return;
+
+       dest = l->data;
+       dest->cnt--;
+
+       if (dest->cnt == 0) {
+               net.dest = g_list_remove(net.dest, dest);
+               g_free(dest);
+       }
+}
+
+struct build_whitelist {
+       uint8_t len;
+       uint8_t data[12];
+};
+
+static void whitefilter_add(gpointer data, gpointer user_data)
+{
+       struct mesh_destination *dest = data;
+       struct build_whitelist *white = user_data;
+
+       if (white->len == 0)
+               white->data[white->len++] = FILTER_ADD;
+
+       put_be16(dest->dst, white->data + white->len);
+       white->len += 2;
+
+       if (white->len > (sizeof(white->data) - sizeof(uint16_t))) {
+               net_ctl_msg_send(0, 0, 0, white->data, white->len);
+               white->len = 0;
+       }
+}
+
+static void setup_whitelist()
+{
+       struct build_whitelist white;
+
+       white.len = 0;
+
+       /* Enable (and Clear) Proxy Whitelist */
+       white.data[white.len++] = FILTER_SETUP;
+       white.data[white.len++] = WHITELIST_FILTER;
+
+       net_ctl_msg_send(0, 0, 0, white.data, white.len);
+
+       white.len = 0;
+       g_list_foreach(net.dest, whitefilter_add, &white);
+
+       if (white.len)
+               net_ctl_msg_send(0, 0, 0, white.data, white.len);
+}
+
+static void beacon_update(bool first, bool iv_update, uint32_t iv_index)
+{
+
+       /* Enforcement of 96 hour and 192 hour IVU time windows */
+       if (iv_update && !net.iv_update) {
+               bt_shell_printf("iv_upd_state = IV_UPD_UPDATING\n");
+               net.iv_upd_state = IV_UPD_UPDATING;
+               /* TODO: Start timer to enforce IV Update parameters */
+       } else if (first) {
+               if (iv_update)
+                       net.iv_upd_state = IV_UPD_UPDATING;
+               else
+                       net.iv_upd_state = IV_UPD_NORMAL;
+
+               bt_shell_printf("iv_upd_state = IV_UPD_%s\n",
+                               iv_update ? "UPDATING" : "NORMAL");
+
+       } else if (iv_update && iv_index != net.iv_index) {
+               bt_shell_printf("IV Update too soon -- Rejecting\n");
+               return;
+       }
+
+       if (iv_index > net.iv_index ||
+                       iv_update != net.iv_update) {
+
+               /* Don't reset our seq_num unless
+                * we start using new iv_index */
+               if (!(iv_update && (net.iv_index + 1 == iv_index))) {
+                       net.seq_num = 0;
+                       net.seq_num_reserved = 100;
+               }
+       }
+
+       if (!net.seq_num || net.iv_index != iv_index ||
+                       net.iv_update != iv_update) {
+
+               if (net.seq_num_reserved <= net.seq_num)
+                       net.seq_num_reserved = net.seq_num + 100;
+
+               prov_db_local_set_iv_index(iv_index, iv_update,
+                               net.provisioner);
+               prov_db_local_set_seq_num(net.seq_num_reserved);
+       }
+
+       net.iv_index = iv_index;
+       net.iv_update = iv_update;
+
+       if (first) {
+               /* Must be done once per Proxy Connection after Beacon RXed */
+               setup_whitelist();
+               if (net.open_cb)
+                       net.open_cb(0);
+       }
+}
+
+static bool process_beacon(uint8_t *data, uint8_t size)
+{
+       struct mesh_net_key *net_key;
+       struct net_key_parts *key_part;
+       bool rxed_iv_update, rxed_key_refresh, iv_update;
+       bool  my_krf;
+       uint32_t rxed_iv_index, iv_index;
+       uint64_t cmac;
+
+       if (size != 22)
+               return false;
+
+       rxed_key_refresh = (data[1] & 0x01) == 0x01;
+       iv_update = rxed_iv_update = (data[1] & 0x02) == 0x02;
+       iv_index = rxed_iv_index = get_be32(data + 10);
+
+       /* Inhibit recognizing iv_update true-->false
+        * if we have outbound SAR messages in flight */
+       if (net.msg_out != NULL) {
+               if (net.iv_update && !rxed_iv_update)
+                       iv_update = true;
+       }
+
+       /* Don't bother going further if nothing has changed */
+       if (iv_index == net.iv_index && iv_update == net.iv_update &&
+                       net.iv_upd_state != IV_UPD_INIT)
+               return true;
+
+       /* Find key we are using for SNBs */
+       net_key = find_net_key_by_id(data + 2);
+
+       if (net_key == NULL)
+               return false;
+
+       /* We are Provisioner, and control the key_refresh flag */
+       if (rxed_key_refresh != !!(net_key->phase == 2))
+               return false;
+
+       if (net_key->phase != 2) {
+               my_krf = false;
+               key_part = &net_key->current;
+       } else {
+               my_krf = true;
+               key_part = &net_key->new;
+       }
+
+       /* Ignore for incorrect KR state */
+       if (memcmp(key_part->net_id, data + 2, 8))
+               return false;
+
+       if ((net.iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+                       (iv_index < net.iv_index)) {
+               bt_shell_printf("iv index outside range\n");
+               return false;
+       }
+
+       /* Any behavioral changes must pass CMAC test */
+       if (!mesh_crypto_beacon_cmac(key_part->beacon_key, key_part->net_id,
+                               rxed_iv_index, my_krf,
+                               rxed_iv_update, &cmac)) {
+               return false;
+       }
+
+       if (cmac != get_be64(data + 14))
+               return false;
+
+       if (iv_update && (net.iv_upd_state > IV_UPD_UPDATING)) {
+               if (iv_index != net.iv_index) {
+                       bt_shell_printf("Update too soon -- Rejecting\n");
+               }
+               /* Silently ignore old beacons */
+               return true;
+       }
+
+       beacon_update(net.iv_upd_state == IV_UPD_INIT, iv_update, iv_index);
+
+       return true;
+}
+
+struct decode_params {
+       struct mesh_net_key     *net_key;
+       uint8_t                 *packet;
+       uint32_t                iv_index;
+       uint8_t                 size;
+       bool                    proxy;
+};
+
+static void try_decode(gpointer data, gpointer user_data)
+{
+       struct mesh_net_key *net_key = data;
+       struct decode_params *decode = user_data;
+       uint8_t nid = decode->packet[0] & 0x7f;
+       uint8_t tmp[29];
+       bool status = false;
+
+       if (decode->net_key)
+               return;
+
+       if (net_key->current.nid == nid)
+               status = mesh_crypto_packet_decode(decode->packet,
+                               decode->size, decode->proxy, tmp,
+                               decode->iv_index,
+                               net_key->current.enc_key,
+                               net_key->current.privacy_key);
+
+       if (!status && net_key->new.nid == nid)
+               status = mesh_crypto_packet_decode(decode->packet,
+                               decode->size, decode->proxy, tmp,
+                               decode->iv_index,
+                               net_key->new.enc_key,
+                               net_key->new.privacy_key);
+
+       if (status) {
+               decode->net_key = net_key;
+               memcpy(decode->packet, tmp, decode->size);
+               return;
+       }
+}
+
+static struct mesh_net_key *net_packet_decode(bool proxy, uint32_t iv_index,
+                               uint8_t *packet, uint8_t size)
+{
+       struct decode_params decode = {
+               .proxy = proxy,
+               .iv_index = iv_index,
+               .packet = packet,
+               .size = size,
+               .net_key = NULL,
+       };
+
+       g_list_foreach(net_keys, try_decode, &decode);
+
+       return decode.net_key;
+}
+
+static void flush_sar(GList **list, struct mesh_sar_msg *sar)
+{
+       *list = g_list_remove(*list, sar);
+
+       if (sar->msg_to)
+               g_source_remove(sar->msg_to);
+
+       if (sar->ack_to)
+               g_source_remove(sar->ack_to);
+
+       g_free(sar);
+}
+
+static void flush_sar_list(GList **list)
+{
+       struct mesh_sar_msg *sar;
+       GList *l = g_list_first(*list);
+
+       while (l) {
+               sar = l->data;
+               flush_sar(list, sar);
+               l = g_list_first(*list);
+       }
+}
+
+static void flush_pkt_list(GList **list)
+{
+       struct mesh_pkt *pkt;
+       GList *l = g_list_first(*list);
+
+       while (l) {
+               pkt = l->data;
+               *list = g_list_remove(*list, pkt);
+               g_free(pkt);
+       }
+}
+
+static void resend_unacked_segs(gpointer data, gpointer user_data)
+{
+       struct mesh_sar_msg *sar = data;
+
+       if (sar->activity_cnt)
+               resend_segs(sar);
+}
+
+static void send_pkt_cmplt(DBusMessage *message, void *user_data)
+{
+       struct mesh_pkt *pkt = user_data;
+       GList *l = g_list_first(net.pkt_out);
+
+       if (l && user_data == l->data) {
+               net.pkt_out = g_list_delete_link(net.pkt_out, l);
+               g_free(pkt);
+       } else {
+               /* This is a serious error, and probable memory leak */
+               bt_shell_printf("ERR: send_pkt_cmplt %p not head of queue\n", pkt);
+       }
+
+       l = g_list_first(net.pkt_out);
+
+       if (l == NULL) {
+               /* If queue is newly empty, resend all SAR outbound packets */
+               g_list_foreach(net.msg_out, resend_unacked_segs, NULL);
+               return;
+       }
+
+       pkt = l->data;
+
+       mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
+                       send_pkt_cmplt, pkt);
+}
+
+static void send_mesh_pkt(struct mesh_pkt *pkt)
+{
+       bool queued = !!(net.pkt_out);
+
+       net.pkt_out = g_list_append(net.pkt_out, pkt);
+
+       if (queued)
+               return;
+
+       mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
+                       send_pkt_cmplt, pkt);
+}
+
+static uint32_t get_next_seq()
+{
+       uint32_t this_seq = net.seq_num++;
+
+       if (net.seq_num + 32 >= net.seq_num_reserved) {
+               net.seq_num_reserved = net.seq_num + 100;
+               prov_db_local_set_seq_num(net.seq_num_reserved);
+       }
+
+       return this_seq;
+}
+
+static void send_seg(struct mesh_sar_msg *sar, uint8_t seg)
+{
+       struct mesh_net_key *net_key;
+       struct net_key_parts *part;
+       struct mesh_pkt *pkt;
+       uint8_t *data;
+
+       net_key = find_net_key_by_idx(sar->net_idx);
+
+       if (net_key == NULL)
+               return;
+
+       /* Choose which components to use to secure pkt */
+       if (net_key->phase == 2 && net_key->new.nid != 0xff)
+               part = &net_key->new;
+       else
+               part = &net_key->current;
+
+       pkt = g_new0(struct mesh_pkt, 1);
+
+       if (pkt == NULL)
+               return;
+
+       /* leave extra byte at start for GATT Proxy type */
+       data = pkt->data + 1;
+
+       SET_PKT_NID(data, part->nid);
+       SET_PKT_IVI(data, sar->iv_index & 1);
+       SET_PKT_CTL(data, sar->ctl);
+       SET_PKT_TTL(data, sar->ttl);
+       SET_PKT_SEQ(data, get_next_seq());
+       SET_PKT_SRC(data, sar->src);
+       SET_PKT_DST(data, sar->dst);
+       SET_PKT_SEGMENTED(data, sar->segmented);
+
+       if (sar->ctl)
+               SET_PKT_OPCODE(data, sar->data[0]);
+       else
+               SET_PKT_AKF_AID(data, sar->akf_aid);
+
+       if (sar->segmented) {
+
+               if (!sar->ctl)
+                       SET_PKT_SZMIC(data, sar->szmic);
+
+               SET_PKT_SEQ0(data, sar->seqAuth);
+               SET_PKT_SEGO(data, seg);
+               SET_PKT_SEGN(data, sar->segN);
+
+               memcpy(PKT_TRANS(data) + 4,
+                               sar->data + sar->ctl + (seg * 12), 12);
+
+               pkt->len = 9 + 4;
+
+               if (sar->segN == seg)
+                       pkt->len += (sar->len - sar->ctl) % 12;
+
+               if (pkt->len == (9 + 4))
+                       pkt->len += 12;
+
+       } else {
+               memcpy(PKT_TRANS(data) + 1,
+                               sar->data + sar->ctl, 15);
+
+               pkt->len = 9 + 1 + sar->len - sar->ctl;
+       }
+
+       pkt->len += (sar->ctl ? 8 : 4);
+       mesh_crypto_packet_encode(data, pkt->len,
+                       part->enc_key,
+                       sar->iv_index,
+                       part->privacy_key);
+
+
+       /* Prepend GATT_Proxy packet type */
+       if (sar->proxy)
+               pkt->data[0] = PROXY_CONFIG_PDU;
+       else
+               pkt->data[0] = PROXY_NETWORK_PDU;
+
+       pkt->len++;
+
+       send_mesh_pkt(pkt);
+}
+
+static void resend_segs(struct mesh_sar_msg *sar)
+{
+       uint32_t ack = 1;
+       uint8_t i;
+
+       sar->activity_cnt = 0;
+
+       for (i = 0; i <= sar->segN; i++, ack <<= 1) {
+               if (!(ack & sar->ack))
+                       send_seg(sar, i);
+       }
+}
+
+static bool ack_rxed(bool to, uint16_t src, uint16_t dst, bool obo,
+                               uint16_t seq0, uint32_t ack_flags)
+{
+       struct mesh_sar_msg *sar = find_sar_out_by_dst(src);
+       uint32_t full_ack;
+
+       /* Silently ignore unknown (stale?) ACKs */
+       if (sar == NULL)
+               return true;
+
+       full_ack = 0xffffffff >> (31 - sar->segN);
+
+       sar->ack |= (ack_flags & full_ack);
+
+       if (sar->ack == full_ack) {
+               /* Outbound message 100% received by remote node */
+               flush_sar(&net.msg_out, sar);
+               return true;
+       }
+
+       /* Because we are GATT, and slow, only resend PKTs if it is
+        * time *and* our outbound PKT queue is empty.  */
+       sar->activity_cnt++;
+
+       if (net.pkt_out == NULL)
+               resend_segs(sar);
+
+       return true;
+}
+
+static bool proxy_ctl_rxed(uint16_t net_idx, uint32_t iv_index,
+               uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+               uint8_t *trans, uint16_t len)
+{
+       if (len < 1)
+               return false;
+
+       switch(trans[0]) {
+               case FILTER_STATUS:
+                       if (len != 4)
+                               return false;
+
+                       net.blacklist = !!(trans[1] == BLACKLIST_FILTER);
+                       bt_shell_printf("Proxy %slist filter length: %d\n",
+                                       net.blacklist ? "Black" : "White",
+                                       get_be16(trans + 2));
+
+                       return true;
+
+               default:
+                       return false;
+       }
+
+       return false;
+}
+
+static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
+               uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+               uint8_t *trans, uint16_t len)
+{
+       /* TODO: Handle control messages */
+
+       /* Per Mesh Profile 3.6.5.10 */
+       if (trans[0] == NET_OP_HEARTBEAT) {
+               uint16_t feat = get_be16(trans + 2);
+
+               bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
+                               TTL: %2.2x feat: %s%s%s%s\n",
+                               src, dst, trans[1],
+                               (feat & MESH_FEATURE_RELAY) ? "relay " : "",
+                               (feat & MESH_FEATURE_PROXY) ? "proxy " : "",
+                               (feat & MESH_FEATURE_FRIEND) ? "friend " : "",
+                               (feat & MESH_FEATURE_LPN) ? "lpn" : "");
+               return true;
+       }
+
+       bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
+                       src, dst, len);
+       print_byte_array("msg: ", trans, len);
+       return false;
+}
+
+struct decrypt_params {
+       uint8_t         *nonce;
+       uint8_t         *aad;
+       uint8_t         *out_msg;
+       uint8_t         *trans;
+       uint32_t        iv_index;
+       uint32_t        seq_num;
+       uint16_t        src;
+       uint16_t        dst;
+       uint16_t        len;
+       uint16_t        net_idx;
+       uint16_t        app_idx;
+       uint8_t         akf_aid;
+       bool            szmic;
+};
+
+
+static void try_decrypt(gpointer data, gpointer user_data)
+{
+       struct mesh_app_key *app_key = data;
+       struct decrypt_params *decrypt = user_data;
+       size_t mic_size = decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+       bool status = false;
+
+       /* Already done... Nothing to do */
+       if (decrypt->app_idx != APP_IDX_INVALID)
+               return;
+
+       /* Don't decrypt on Appkeys not owned by this NetKey */
+       if (app_key->net_idx != decrypt->net_idx)
+               return;
+
+       /* Test and decrypt against current key copy */
+       if (app_key->current.akf_aid == decrypt->akf_aid)
+               status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
+                               app_key->current.key,
+                               decrypt->aad, decrypt->aad ? 16 : 0,
+                               decrypt->trans, decrypt->len,
+                               decrypt->out_msg, NULL, mic_size);
+
+       /* Test and decrypt against new key copy */
+       if (!status && app_key->new.akf_aid == decrypt->akf_aid)
+               status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
+                               app_key->new.key,
+                               decrypt->aad, decrypt->aad ? 16 : 0,
+                               decrypt->trans, decrypt->len,
+                               decrypt->out_msg, NULL, mic_size);
+
+       /* If successful, terminate with successful App IDX */
+       if (status)
+               decrypt->app_idx = app_key->generic.idx;
+}
+
+static uint16_t access_pkt_decrypt(uint8_t *nonce, uint8_t *aad,
+               uint16_t net_idx, uint8_t akf_aid, bool szmic,
+               uint8_t *trans, uint16_t len)
+{
+       uint8_t *out_msg;
+       struct decrypt_params decrypt = {
+               .nonce = nonce,
+               .aad = aad,
+               .net_idx = net_idx,
+               .akf_aid = akf_aid,
+               .szmic = szmic,
+               .trans = trans,
+               .len = len,
+               .app_idx = APP_IDX_INVALID,
+       };
+
+       out_msg = g_malloc(len);
+
+       if (out_msg == NULL)
+               return false;
+
+       decrypt.out_msg = out_msg;
+
+       g_list_foreach(app_keys, try_decrypt, &decrypt);
+
+       if (decrypt.app_idx != APP_IDX_INVALID)
+               memcpy(trans, out_msg, len);
+
+       g_free(out_msg);
+
+       return decrypt.app_idx;
+}
+
+static bool access_rxed(uint8_t *nonce, uint16_t net_idx,
+               uint32_t iv_index, uint32_t seq_num,
+               uint16_t src, uint16_t dst,
+               uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
+{
+       uint16_t app_idx = access_pkt_decrypt(nonce, NULL,
+                       net_idx, akf_aid, szmic, trans, len);
+
+       if (app_idx != APP_IDX_INVALID) {
+               len -= szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+
+               node_local_data_handler(src, dst, iv_index, seq_num,
+                               app_idx, trans, len);
+               return true;
+       }
+
+       return false;
+}
+
+static void try_virt_decrypt(gpointer data, gpointer user_data)
+{
+       struct mesh_virt_addr *virt = data;
+       struct decrypt_params *decrypt = user_data;
+
+       if (decrypt->app_idx != APP_IDX_INVALID || decrypt->dst != virt->va16)
+               return;
+
+       decrypt->app_idx = access_pkt_decrypt(decrypt->nonce,
+                       virt->va128,
+                       decrypt->net_idx, decrypt->akf_aid,
+                       decrypt->szmic, decrypt->trans, decrypt->len);
+
+       if (decrypt->app_idx != APP_IDX_INVALID) {
+               uint16_t len = decrypt->len;
+
+               len -= decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+
+               node_local_data_handler(decrypt->src, virt->va32,
+                               decrypt->iv_index, decrypt->seq_num,
+                               decrypt->app_idx, decrypt->trans, len);
+       }
+}
+
+static bool virtual_rxed(uint8_t *nonce, uint16_t net_idx,
+               uint32_t iv_index, uint32_t seq_num,
+               uint16_t src, uint16_t dst,
+               uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
+{
+       struct decrypt_params decrypt = {
+               .nonce = nonce,
+               .net_idx = net_idx,
+               .iv_index = iv_index,
+               .seq_num = seq_num,
+               .src = dst,
+               .dst = dst,
+               .akf_aid = akf_aid,
+               .szmic = szmic,
+               .trans = trans,
+               .len = len,
+               .app_idx = APP_IDX_INVALID,
+       };
+
+       /* Cycle through known virtual addresses */
+       g_list_foreach(virt_addrs, try_virt_decrypt, &decrypt);
+
+       if (decrypt.app_idx != APP_IDX_INVALID)
+               return true;
+
+       return false;
+}
+
+static bool msg_rxed(uint16_t net_idx, uint32_t iv_index, bool szmic,
+               uint8_t ttl, uint32_t seq_num, uint32_t seq_auth,
+               uint16_t src, uint16_t dst,
+               uint8_t *trans, uint16_t len)
+{
+       uint8_t akf_aid = TRANS_AKF_AID(trans);
+       bool result;
+       size_t mic_size = szmic ? sizeof(uint64_t) : sizeof(uint32_t);
+       uint8_t nonce[13];
+       uint8_t *dev_key;
+       uint8_t *out = NULL;
+
+       if (!TRANS_AKF(trans)) {
+               /* Compose Nonce */
+               result = mesh_crypto_device_nonce(seq_auth, src, dst,
+                               iv_index, szmic, nonce);
+
+               if (!result) return false;
+
+               out = g_malloc0(TRANS_LEN(trans, len));
+               if (out == NULL) return false;
+
+               /* If we are provisioner, we probably RXed on remote Dev Key */
+               if (net.provisioner) {
+                       dev_key = node_get_device_key(node_find_by_addr(src));
+
+                       if (dev_key == NULL)
+                               goto local_dev_key;
+               } else
+                       goto local_dev_key;
+
+               result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
+                               NULL, 0,
+                               TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
+                               out, NULL, mic_size);
+
+               if (result) {
+                       node_local_data_handler(src, dst,
+                                       iv_index, seq_num, APP_IDX_DEV,
+                                       out, TRANS_LEN(trans, len) - mic_size);
+                       goto done;
+               }
+
+local_dev_key:
+               /* Always fallback to the local Dev Key */
+               dev_key = node_get_device_key(node_get_local_node());
+
+               if (dev_key == NULL)
+                       goto done;
+
+               result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
+                               NULL, 0,
+                               TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
+                               out, NULL, mic_size);
+
+               if (result) {
+                       node_local_data_handler(src, dst,
+                                       iv_index, seq_num, APP_IDX_DEV,
+                                       out, TRANS_LEN(trans, len) - mic_size);
+                       goto done;
+               }
+
+               goto done;
+       }
+
+       result = mesh_crypto_application_nonce(seq_auth, src, dst,
+                       iv_index, szmic, nonce);
+
+       if (!result) goto done;
+
+       /* If Virtual destination wrap the Access decoder with Virtual */
+       if (IS_VIRTUAL(dst)) {
+               result = virtual_rxed(nonce, net_idx, iv_index, seq_num,
+                               src, dst, akf_aid, szmic,
+                               TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));
+               goto done;
+       }
+
+       /* Try all matching App Keys until success or exhaustion */
+       result = access_rxed(nonce, net_idx, iv_index, seq_num,
+                       src, dst, akf_aid, szmic,
+                       TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));
+
+done:
+       if (out != NULL)
+               g_free(out);
+
+       return result;
+}
+
+static void send_sar_ack(struct mesh_sar_msg *sar)
+{
+       uint8_t ack[7];
+
+       sar->activity_cnt = 0;
+
+       memset(ack, 0, sizeof(ack));
+       SET_TRANS_OPCODE(ack, NET_OP_SEG_ACKNOWLEDGE);
+       SET_TRANS_SEQ0(ack, sar->seqAuth);
+       SET_TRANS_ACK(ack, sar->ack);
+
+       net_ctl_msg_send(0xff, sar->dst, sar->src, ack, sizeof(ack));
+}
+
+static gboolean sar_out_ack_timeout(void *user_data)
+{
+       struct mesh_sar_msg *sar = user_data;
+
+       sar->activity_cnt++;
+
+       /* Because we are GATT, and slow, only resend PKTs if it is
+        * time *and* our outbound PKT queue is empty.  */
+       if (net.pkt_out == NULL)
+               resend_segs(sar);
+
+       /* Only add resent SAR pkts to empty queue */
+       return true;
+}
+
+static gboolean sar_out_msg_timeout(void *user_data)
+{
+       struct mesh_sar_msg *sar = user_data;
+
+       /* msg_to will expire when we return false */
+       sar->msg_to = 0;
+
+       flush_sar(&net.msg_out, sar);
+
+       return false;
+}
+
+static gboolean sar_in_ack_timeout(void *user_data)
+{
+       struct mesh_sar_msg *sar = user_data;
+       uint32_t full_ack = 0xffffffff >> (31 - sar->segN);
+
+       if (sar->activity_cnt || sar->ack != full_ack)
+               send_sar_ack(sar);
+
+       return true;
+}
+
+static gboolean sar_in_msg_timeout(void *user_data)
+{
+       struct mesh_sar_msg *sar = user_data;
+
+       /* msg_to will expire when we return false */
+       sar->msg_to = 0;
+
+       flush_sar(&net.sar_in, sar);
+
+       return false;
+}
+
+static uint32_t calc_seqAuth(uint32_t seq_num, uint8_t *trans)
+{
+       uint32_t seqAuth = seq_num & ~0x1fff;
+
+       seqAuth |= TRANS_SEQ0(trans);
+
+       return seqAuth;
+}
+
+static bool seg_rxed(uint16_t net_idx, uint32_t iv_index, bool ctl,
+               uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
+               uint8_t *trans, uint16_t len)
+{
+       struct mesh_sar_msg *sar;
+       uint32_t seqAuth = calc_seqAuth(seq_num, trans);
+       uint8_t segN, segO;
+       uint32_t old_ack, full_ack, last_ack_mask;
+       bool send_ack, result = false;
+
+       segN = TRANS_SEGN(trans);
+       segO = TRANS_SEGO(trans);
+
+       /* Only support single incoming SAR'd message per SRC */
+       sar = find_sar_in_by_src(src);
+
+       /* Reuse existing SAR structure if appropriate */
+       if (sar) {
+               uint64_t iv_seqAuth = (uint64_t)iv_index << 32 | seqAuth;
+               uint64_t old_iv_seqAuth = (uint64_t)sar->iv_index << 32 |
+                       sar->seqAuth;
+               if (old_iv_seqAuth < iv_seqAuth) {
+
+                       flush_sar(&net.sar_in, sar);
+                       sar = NULL;
+
+               } else if (old_iv_seqAuth > iv_seqAuth) {
+
+                       /* New segment is Stale. Silently ignore */
+                       return false;
+
+               } else if (segN != sar->segN) {
+
+                       /* Remote side sent conflicting data: abandon */
+                       flush_sar(&net.sar_in, sar);
+                       sar = NULL;
+
+               }
+       }
+
+       if (sar == NULL) {
+               sar = g_malloc0(sizeof(*sar) + (12 * segN));
+
+               if (sar == NULL)
+                       return false;
+
+               sar->net_idx = net_idx;
+               sar->iv_index = iv_index;
+               sar->ctl = ctl;
+               sar->ttl = ttl;
+               sar->seqAuth = seqAuth;
+               sar->src = src;
+               sar->dst = dst;
+               sar->segmented = true;
+               sar->szmic = TRANS_SZMIC(trans);
+               sar->segN = segN;
+
+               /* In all cases, the reassembled packet should begin with the
+                * same first octet of all segments, minus the SEGMENTED flag */
+               sar->data[0] = trans[0] & 0x7f;
+
+               net.sar_in = g_list_append(net.sar_in, sar);
+
+               /* Setup expiration timers */
+               if (IS_UNICAST(dst))
+                       sar->ack_to = g_timeout_add(5000,
+                                       sar_in_ack_timeout, sar);
+
+               sar->msg_to = g_timeout_add(60000, sar_in_msg_timeout, sar);
+       }
+
+       /* If last segment, calculate full msg size */
+       if (segN == segO)
+               sar->len = (segN * 12) + len - 3;
+
+       /* Copy to correct offset */
+       memcpy(sar->data + 1 + (12 * segO), trans + 4, 12);
+
+       full_ack = 0xffffffff >> (31 - segN);
+       last_ack_mask = 0xffffffff << segO;
+       old_ack = sar->ack;
+       sar->ack |= 1 << segO;
+       send_ack = false;
+
+       /* Determine if we should forward message */
+       if (sar->ack == full_ack && old_ack != full_ack) {
+
+               /* First time we have seen this complete message */
+               send_ack = true;
+
+               if (ctl)
+                       result = ctl_rxed(sar->net_idx, sar->iv_index,
+                                       sar->ttl, sar->seqAuth, sar->src,
+                                       sar->dst, sar->data, sar->len);
+               else
+                       result = msg_rxed(sar->net_idx, sar->iv_index,
+                                       sar->szmic, sar->ttl,
+                                       seq_num, sar->seqAuth, sar->src,
+                                       sar->dst, sar->data, sar->len);
+       }
+
+       /* Never Ack Group addressed SAR messages */
+       if (!IS_UNICAST(dst))
+               return result;
+
+       /* Tickle the ACK system so it knows we are still RXing segments */
+       sar->activity_cnt++;
+
+       /* Determine if we should ACK */
+       if (old_ack == sar->ack)
+               /* Let the timer generate repeat ACKs as needed */
+               send_ack = false;
+       else if ((last_ack_mask & sar->ack) == (last_ack_mask & full_ack))
+               /* If this was largest segO outstanding segment, we ACK */
+               send_ack = true;
+
+       if (send_ack)
+               send_sar_ack(sar);
+
+       return result;
+}
+
+bool net_data_ready(uint8_t *msg, uint8_t len)
+{
+       uint8_t type = *msg++;
+       uint32_t iv_index = net.iv_index;
+       struct mesh_net_key *net_key;
+
+       if (len-- < 10) return false;
+
+       if (type == PROXY_MESH_BEACON)
+               return process_beacon(msg, len);
+       else if (type > PROXY_CONFIG_PDU)
+               return false;
+
+       /* RXed iv_index must be equal or 1 less than local iv_index */
+       /* With the clue being high-order bit of first octet */
+       if (!!(iv_index & 0x01) != !!(msg[0] & 0x80)) {
+               if (iv_index)
+                       iv_index--;
+               else
+                       return false;
+       }
+
+       net_key = net_packet_decode(type == PROXY_CONFIG_PDU,
+                       iv_index, msg, len);
+
+       if (net_key == NULL)
+               return false;
+
+       /* CTL packets have 64 bit network MIC, otherwise 32 bit MIC */
+       len -= PKT_CTL(msg) ? sizeof(uint64_t) : sizeof(uint32_t);
+
+       if (type == PROXY_CONFIG_PDU) {
+
+               /* Proxy Configuration DST messages must be 0x0000 */
+               if (PKT_DST(msg))
+                       return false;
+
+               return proxy_ctl_rxed(net_key->generic.idx,
+                               iv_index, PKT_TTL(msg), PKT_SEQ(msg),
+                               PKT_SRC(msg), PKT_DST(msg),
+                               PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+       } if (PKT_CTL(msg) && PKT_OPCODE(msg) == NET_OP_SEG_ACKNOWLEDGE) {
+
+               return ack_rxed(false, PKT_SRC(msg), PKT_DST(msg),
+                               PKT_OBO(msg), PKT_SEQ0(msg), PKT_ACK(msg));
+
+       } else if (PKT_SEGMENTED(msg)) {
+
+               return seg_rxed(net_key->generic.idx, iv_index, PKT_CTL(msg),
+                               PKT_TTL(msg), PKT_SEQ(msg),
+                               PKT_SRC(msg), PKT_DST(msg),
+                               PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+       } else if (!PKT_CTL(msg)){
+
+               return msg_rxed(net_key->generic.idx,
+                               iv_index, false, PKT_TTL(msg), PKT_SEQ(msg),
+                               PKT_SEQ(msg), PKT_SRC(msg), PKT_DST(msg),
+                               PKT_TRANS(msg), PKT_TRANS_LEN(len));
+       } else {
+
+               return ctl_rxed(net_key->generic.idx,
+                               iv_index, PKT_TTL(msg), PKT_SEQ(msg),
+                               PKT_SRC(msg), PKT_DST(msg),
+                               PKT_TRANS(msg), PKT_TRANS_LEN(len));
+
+       }
+
+       return false;
+}
+
+bool net_session_open(GDBusProxy *data_in, bool provisioner,
+                                       net_mesh_session_open_callback cb)
+{
+       if (net.proxy_in)
+               return false;
+
+       net.proxy_in = data_in;
+       net.iv_upd_state = IV_UPD_INIT;
+       net.blacklist = false;
+       net.provisioner = provisioner;
+       net.open_cb = cb;
+       flush_pkt_list(&net.pkt_out);
+       return true;
+}
+
+void net_session_close(GDBusProxy *data_in)
+{
+       if (net.proxy_in == data_in)
+               net.proxy_in = NULL;
+
+       flush_sar_list(&net.sar_in);
+       flush_sar_list(&net.msg_out);
+       flush_pkt_list(&net.pkt_out);
+}
+
+bool net_register_unicast(uint16_t unicast, uint8_t count)
+{
+       /* TODO */
+       return true;
+}
+
+bool net_register_group(uint16_t group_addr)
+{
+       /* TODO */
+       return true;
+}
+
+uint32_t net_register_virtual(uint8_t buf[16])
+{
+       /* TODO */
+       return 0;
+}
+
+static bool get_enc_keys(uint16_t app_idx, uint16_t dst,
+               uint8_t *akf_aid, uint8_t **app_enc_key,
+               uint16_t *net_idx)
+{
+       if (app_idx == APP_IDX_DEV) {
+               struct mesh_node *node;
+               uint8_t *enc_key = NULL;
+
+               if (net.provisioner) {
+                       /* Default to Remote Device Key when Provisioner */
+                       node = node_find_by_addr(dst);
+                       enc_key = node_get_device_key(node);
+               }
+
+               if (enc_key == NULL) {
+                       /* Use Local node Device Key */
+                       node = node_get_local_node();
+                       enc_key = node_get_device_key(node);
+               }
+
+               if (enc_key == NULL || node == NULL)
+                       return false;
+
+               if (akf_aid) *akf_aid = 0;
+               if (app_enc_key) *app_enc_key = enc_key;
+               if (net_idx) *net_idx = node_get_primary_net_idx(node);
+
+       } else {
+               struct mesh_app_key *app_key = find_app_key_by_idx(app_idx);
+               struct mesh_net_key *net_key;
+               bool phase_two;
+
+
+               if (app_key == NULL)
+                       return false;
+
+               net_key = find_net_key_by_idx(app_key->net_idx);
+
+               if (net_key == NULL)
+                       return false;
+
+               if (net_idx) *net_idx = app_key->net_idx;
+
+               phase_two = !!(net_key->phase == 2);
+
+               if (phase_two && app_key->new.akf_aid != 0xff) {
+                       if (app_enc_key) *app_enc_key = app_key->new.key;
+                       if (akf_aid) *akf_aid = app_key->new.akf_aid;
+               } else {
+                       if (app_enc_key) *app_enc_key = app_key->current.key;
+                       if (akf_aid) *akf_aid = app_key->current.akf_aid;
+               }
+       }
+
+       return true;
+}
+
+bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,
+                                       uint8_t *buf, uint16_t len)
+{
+       struct mesh_node *node = node_get_local_node();
+       struct mesh_sar_msg sar_ctl;
+
+       /* For simplicity, we will reject segmented OB CTL messages */
+       if (len > 12 || node == NULL || buf == NULL || buf[0] & 0x80)
+               return false;
+
+       if (!src) {
+               src = node_get_primary(node);
+
+               if (!src)
+                       return false;
+       }
+
+       if (ttl == 0xff)
+               ttl = net.default_ttl;
+
+       memset(&sar_ctl, 0, sizeof(sar_ctl));
+
+       if (!dst)
+               sar_ctl.proxy = true;
+
+       /* Get the default net_idx for remote device (or local) */
+       get_enc_keys(APP_IDX_DEV, dst, NULL, NULL, &sar_ctl.net_idx);
+       sar_ctl.ctl = true;
+       sar_ctl.iv_index = net.iv_index - net.iv_update;
+       sar_ctl.ttl = ttl;
+       sar_ctl.src = src;
+       sar_ctl.dst = dst;
+       sar_ctl.len = len;
+       memcpy(sar_ctl.data, buf, len);
+       send_seg(&sar_ctl, 0);
+
+       return true;
+}
+
+bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
+                               uint16_t app_idx, uint8_t *buf, uint16_t len)
+{
+       struct mesh_node *node = node_get_local_node();
+       struct mesh_sar_msg *sar;
+       uint8_t *app_enc_key = NULL;
+       uint8_t *aad = NULL;
+       uint32_t mic32;
+       uint8_t aad_len = 0;
+       uint8_t i, j, ackless_retries = 0;
+       uint8_t segN, akf_aid;
+       uint16_t net_idx;
+       bool result;
+
+       if (len > 384 || node == NULL)
+               return false;
+
+       if (!src)
+               src = node_get_primary(node);
+
+       if (!src || !dst)
+               return false;
+
+       if (ttl == 0xff)
+               ttl = net.default_ttl;
+
+       if (IS_VIRTUAL(dst)) {
+               struct mesh_virt_addr *virt = find_virt_by_dst(dst);
+
+               if (virt == NULL)
+                       return false;
+
+               dst = virt->va16;
+               aad = virt->va128;
+               aad_len = sizeof(virt->va128);
+       }
+
+       result = get_enc_keys(app_idx, dst,
+                       &akf_aid, &app_enc_key, &net_idx);
+
+       if (!result)
+               return false;
+
+       segN = SEG_MAX(len + sizeof(mic32));
+
+       /* Only one ACK required SAR message per destination at a time */
+       if (segN && IS_UNICAST(dst)) {
+               sar = find_sar_out_by_dst(dst);
+
+               if (sar)
+                       flush_sar(&net.msg_out, sar);
+       }
+
+       sar = g_malloc0(sizeof(struct mesh_sar_msg) + (segN * 12));
+
+       if (sar == NULL)
+               return false;
+
+       if (segN)
+               sar->segmented = true;
+
+       sar->ttl = ttl;
+       sar->segN = segN;
+       sar->seqAuth = net.seq_num;
+       sar->iv_index = net.iv_index - net.iv_update;
+       sar->net_idx = net_idx;
+       sar->src = src;
+       sar->dst = dst;
+       sar->akf_aid = akf_aid;
+       sar->len = len + sizeof(uint32_t);
+
+       mesh_crypto_application_encrypt(akf_aid,
+                       sar->seqAuth, src,
+                       dst, sar->iv_index,
+                       app_enc_key,
+                       aad, aad_len,
+                       buf, len,
+                       sar->data, &mic32,
+                       sizeof(uint32_t));
+
+       /* If sending as a segmented message to a non-Unicast (thus non-ACKing)
+        * destination, send each segments multiple times. */
+       if (!IS_UNICAST(dst) && segN)
+               ackless_retries = 4;
+
+       for (j = 0; j <= ackless_retries; j++) {
+               for (i = 0; i <= segN; i++)
+                       send_seg(sar, i);
+       }
+
+       if (IS_UNICAST(dst) && segN) {
+               net.msg_out = g_list_append(net.msg_out, sar);
+               sar->ack_to = g_timeout_add(2000, sar_out_ack_timeout, sar);
+               sar->msg_to = g_timeout_add(60000, sar_out_msg_timeout, sar);
+       } else
+               g_free(sar);
+
+       return true;
+}
+
+bool net_set_default_ttl(uint8_t ttl)
+{
+       if (ttl > 0x7f)
+               return false;
+
+       net.default_ttl = ttl;
+       return true;
+}
+
+uint8_t net_get_default_ttl()
+{
+       return net.default_ttl;
+}
+
+bool net_set_seq_num(uint32_t seq_num)
+{
+       if (seq_num > 0xffffff)
+               return false;
+
+       net.seq_num = seq_num;
+       return true;
+}
+
+uint32_t net_get_seq_num()
+{
+       return net.seq_num;
+}
diff --git a/mesh/net.h b/mesh/net.h
new file mode 100644 (file)
index 0000000..b388d61
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 "gdbus/gdbus.h"
+
+typedef void (*net_mesh_session_open_callback)(int status);
+
+uint32_t net_get_iv_index(bool *iv_update);
+bool net_get_key(uint16_t net_idx, uint8_t *key);
+bool net_get_flags(uint16_t net_idx, uint8_t *out_flags);
+void net_set_iv_index(uint32_t index, bool update);
+uint32_t get_sequence_number(void);
+void set_sequence_number(uint32_t seq_number);
+uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon);
+bool net_add_address_pool(uint16_t min, uint16_t max);
+uint16_t net_obtain_address(uint8_t num_elements);
+bool net_reserve_address_range(uint16_t base, uint8_t num_elements);
+void net_release_address(uint16_t addr, uint8_t num_elements);
+bool net_session_open(GDBusProxy *data_in, bool provisioner,
+                                       net_mesh_session_open_callback cb);
+void net_session_close(GDBusProxy *data_in);
+
+bool net_data_ready(uint8_t *ptr, uint8_t len);
+bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
+                               uint16_t app_idx, uint8_t *buf, uint16_t len);
+bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,
+                                       uint8_t *buf, uint16_t len);
+bool net_set_default_ttl(uint8_t ttl);
+uint8_t net_get_default_ttl(void);
+bool net_set_seq_num(uint32_t seq_num);
+uint32_t net_get_seq_num(void);
+void net_dest_ref(uint16_t dst);
+void net_dest_unref(uint16_t dst);
+bool net_register_unicast(uint16_t unicast, uint8_t count);
+bool net_register_group(uint16_t group_addr);
+uint32_t net_register_virtual(uint8_t buf[16]);
+bool mesh_model_recv(uint16_t app_idx, uint16_t src, uint32_t dst,
+                                               uint8_t *payload, uint16_t len);
diff --git a/mesh/node.c b/mesh/node.c
new file mode 100644 (file)
index 0000000..0a60e79
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+#include "gdbus/gdbus.h"
+#include "mesh/mesh-net.h"
+#include "mesh/config-model.h"
+#include "mesh/node.h"
+#include "mesh/keys.h"
+#include "mesh/gatt.h"
+#include "mesh/net.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+
+struct mesh_model {
+       struct mesh_model_ops cbs;
+       void *user_data;
+       GList *bindings;
+       GList *subscriptions;
+       uint32_t id;
+       struct mesh_publication *pub;
+};
+
+struct mesh_element {
+       GList *models;
+       uint16_t loc;
+       uint8_t index;
+};
+
+struct mesh_node {
+       const char *name;
+       GList *net_keys;
+       GList *app_keys;
+       void *prov;
+       GList *elements;
+       uint32_t iv_index;
+       uint32_t seq_number;
+       uint16_t primary_net_idx;
+       uint16_t primary;
+       uint16_t oob;
+       uint16_t features;
+       uint8_t dev_uuid[16];
+       uint8_t dev_key[16];
+       uint8_t num_ele;
+       uint8_t ttl;
+       bool provisioner;
+       struct mesh_node_composition *comp;
+};
+
+static GList *nodes;
+
+static struct mesh_node *local_node;
+
+static int match_node_unicast(const void *a, const void *b)
+{
+       const struct mesh_node *node = a;
+       uint16_t dst = GPOINTER_TO_UINT(b);
+
+       if (dst >= node->primary &&
+                               dst <= (node->primary + node->num_ele - 1))
+               return 0;
+
+       return -1;
+}
+
+static int match_device_uuid(const void *a, const void *b)
+{
+       const struct mesh_node *node = a;
+       const uint8_t *uuid = b;
+
+       return memcmp(node->dev_uuid, uuid, 16);
+}
+
+static int match_element_idx(const void *a, const void *b)
+{
+       const struct mesh_element *element = a;
+       uint32_t index = GPOINTER_TO_UINT(b);
+
+       return (element->index == index) ? 0 : -1;
+}
+
+static int match_model_id(const void *a, const void *b)
+{
+       const struct mesh_model *model = a;
+       uint32_t id = GPOINTER_TO_UINT(b);
+
+       return (model->id == id) ? 0 : -1;
+}
+
+struct mesh_node *node_find_by_addr(uint16_t addr)
+{
+       GList *l;
+
+       if (!IS_UNICAST(addr))
+               return NULL;
+
+       l = g_list_find_custom(nodes, GUINT_TO_POINTER(addr),
+                       match_node_unicast);
+
+       if (l)
+               return l->data;
+       else
+               return NULL;
+}
+
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16])
+{
+       GList *l;
+
+       l = g_list_find_custom(nodes, uuid, match_device_uuid);
+
+       if (l)
+               return l->data;
+       else
+               return NULL;
+}
+
+struct mesh_node *node_create_new(struct prov_svc_data *prov)
+{
+       struct mesh_node *node;
+
+       if (node_find_by_uuid(prov->dev_uuid))
+               return NULL;
+
+       node = g_malloc0(sizeof(struct mesh_node));
+       if (!node)
+               return NULL;
+
+       memcpy(node->dev_uuid, prov->dev_uuid, 16);
+       node->oob = prov->oob;
+       nodes = g_list_append(nodes, node);
+
+       return node;
+}
+
+struct mesh_node *node_new(void)
+{
+       struct mesh_node *node;
+
+       node = g_malloc0(sizeof(struct mesh_node));
+       if (!node)
+               return NULL;
+
+       nodes = g_list_append(nodes, node);
+
+       return node;
+}
+
+static void model_free(void *data)
+{
+       struct mesh_model *model = data;
+
+       g_list_free(model->bindings);
+       g_list_free(model->subscriptions);
+       g_free(model->pub);
+       g_free(model);
+}
+
+static void element_free(void *data)
+{
+       struct mesh_element *element = data;
+
+       g_list_free_full(element->models, model_free);
+       g_free(element);
+}
+
+static void free_node_resources(void *data)
+{
+       struct mesh_node *node = data;
+       g_list_free(node->net_keys);
+       g_list_free(node->app_keys);
+
+       g_list_free_full(node->elements, element_free);
+
+       if(node->comp)
+               g_free(node->comp);
+
+       g_free(node);
+}
+
+void node_free(struct mesh_node *node)
+{
+       if (!node)
+               return;
+       nodes = g_list_remove(nodes, node);
+       free_node_resources(node);
+}
+
+void node_cleanup(void)
+{
+       g_list_free_full(nodes, free_node_resources);
+       local_node = NULL;
+}
+
+bool node_is_provisioned(struct mesh_node *node)
+{
+       return (!IS_UNASSIGNED(node->primary));
+}
+
+void *node_get_prov(struct mesh_node *node)
+{
+       return node->prov;
+}
+
+void node_set_prov(struct mesh_node *node, void *prov)
+{
+       node->prov = prov;
+}
+
+bool node_app_key_add(struct mesh_node *node, uint16_t idx)
+{
+       uint32_t index;
+       uint16_t net_idx;
+
+       if (!node)
+               return false;
+
+       net_idx = keys_app_key_get_bound(idx);
+       if (net_idx == NET_IDX_INVALID)
+               return false;
+
+       if (!g_list_find(node->net_keys, GUINT_TO_POINTER(net_idx)))
+               return false;
+
+       index = (net_idx << 16) + idx;
+
+       if (g_list_find(node->app_keys, GUINT_TO_POINTER(index)))
+               return false;
+
+       node->app_keys = g_list_append(node->app_keys, GUINT_TO_POINTER(index));
+
+       return true;
+}
+
+bool node_net_key_add(struct mesh_node *node, uint16_t index)
+{
+       if(!node)
+               return false;
+
+       if (g_list_find(node->net_keys, GUINT_TO_POINTER(index)))
+               return false;
+
+       node->net_keys = g_list_append(node->net_keys, GUINT_TO_POINTER(index));
+       return true;
+}
+
+bool node_net_key_delete(struct mesh_node *node, uint16_t index)
+{
+       GList *l;
+
+       if(!node)
+               return false;
+
+       l = g_list_find(node->net_keys, GUINT_TO_POINTER(index));
+       if (!l)
+               return false;
+
+       node->net_keys = g_list_remove(node->net_keys,
+                                               GUINT_TO_POINTER(index));
+       /* TODO: remove all associated app keys and bindings */
+       return true;
+}
+
+bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
+                               uint16_t idx)
+{
+       GList *l;
+       uint32_t index;
+
+       if(!node)
+               return false;
+
+       index = (net_idx << 16) + idx;
+
+       l = g_list_find(node->app_keys, GUINT_TO_POINTER(index));
+       if (!l)
+               return false;
+
+       node->app_keys = g_list_remove(node->app_keys,
+                                       GUINT_TO_POINTER(index));
+       /* TODO: remove all associated bindings */
+       return true;
+}
+
+void node_set_primary(struct mesh_node *node, uint16_t unicast)
+{
+       node->primary = unicast;
+}
+
+uint16_t node_get_primary(struct mesh_node *node)
+{
+       if (!node)
+               return UNASSIGNED_ADDRESS;
+       else
+               return node->primary;
+}
+
+void node_set_device_key(struct mesh_node *node, uint8_t *key)
+
+{
+       if (!node || !key)
+               return;
+
+       memcpy(node->dev_key, key, 16);
+}
+
+uint8_t *node_get_device_key(struct mesh_node *node)
+{
+       if (!node)
+               return NULL;
+       else
+               return node->dev_key;
+}
+
+void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)
+{
+       node->num_ele = num_ele;
+}
+
+uint8_t node_get_num_elements(struct mesh_node *node)
+{
+       return node->num_ele;
+}
+
+GList *node_get_net_keys(struct mesh_node *node)
+{
+       if (!node)
+               return NULL;
+       else
+               return node->net_keys;
+}
+
+GList *node_get_app_keys(struct mesh_node *node)
+{
+       if (!node)
+               return NULL;
+       else
+               return node->app_keys;
+}
+
+bool node_parse_composition(struct mesh_node *node, uint8_t *data, uint16_t len)
+{
+       struct mesh_node_composition *comp;
+       uint16_t features;
+       int i;
+
+       comp = g_malloc0(sizeof(struct mesh_node_composition));
+       if (!comp)
+               return false;
+
+       /* skip page -- We only support Page Zero */
+       data++;
+       len--;
+
+       comp->cid = get_le16(&data[0]);
+       comp->pid = get_le16(&data[2]);
+       comp->vid = get_le16(&data[4]);
+       comp->crpl = get_le16(&data[6]);
+       features = get_le16(&data[8]);
+       data += 10;
+       len -= 10;
+
+       comp->relay = !!(features & MESH_FEATURE_RELAY);
+       comp->proxy = !!(features & MESH_FEATURE_PROXY);
+       comp->friend = !!(features & MESH_FEATURE_FRIEND);
+       comp->lpn =  !!(features & MESH_FEATURE_LPN);
+
+       for (i = 0;  i< node->num_ele; i++) {
+               uint8_t m, v;
+               uint32_t mod_id;
+               uint16_t vendor_id;
+               struct mesh_element *ele;
+               ele = g_malloc0(sizeof(struct mesh_element));
+               if (!ele)
+                       return false;
+
+               ele->index = i;
+               ele->loc = get_le16(data);
+               data += 2;
+               node->elements = g_list_append(node->elements, ele);
+
+               m = *data++;
+               v = *data++;
+               len -= 4;
+
+               while (len >= 2 && m--) {
+                       mod_id = get_le16(data);
+                       /* initialize uppper 16 bits to 0xffff for SIG models */
+                       mod_id |= 0xffff0000;
+                       if (!node_set_model(node, ele->index, mod_id))
+                               return false;
+                       data += 2;
+                       len -= 2;
+               }
+               while (len >= 4 && v--) {
+                       mod_id = get_le16(data + 2);
+                       vendor_id = get_le16(data);
+                       mod_id |= (vendor_id << 16);
+                       if (!node_set_model(node, ele->index, mod_id))
+                               return false;
+                       data += 4;
+                       len -= 4;
+               }
+
+       }
+
+       node->comp = comp;
+       return true;
+}
+
+bool node_set_local_node(struct mesh_node *node)
+{
+       if (local_node) {
+               bt_shell_printf("Local node already registered\n");
+               return false;
+       }
+       net_register_unicast(node->primary, node->num_ele);
+
+       local_node = node;
+       local_node->provisioner = true;
+
+       return true;
+}
+
+struct mesh_node *node_get_local_node()
+{
+       return local_node;
+}
+
+uint16_t node_get_primary_net_idx(struct mesh_node *node)
+{
+       if (node == NULL)
+               return NET_IDX_INVALID;
+
+       return node->primary_net_idx;
+}
+
+static bool deliver_model_data(struct mesh_element* element, uint16_t src,
+                               uint16_t app_idx, uint8_t *data, uint16_t len)
+{
+       GList *l;
+
+       for(l = element->models; l; l = l->next) {
+               struct mesh_model *model = l->data;
+
+               if (!g_list_find(model->bindings, GUINT_TO_POINTER(app_idx)))
+                       continue;
+
+               if (model->cbs.recv &&
+                       model->cbs.recv(src, data, len, model->user_data))
+                       return true;
+       }
+
+       return false;
+}
+
+void node_local_data_handler(uint16_t src, uint32_t dst,
+               uint32_t iv_index, uint32_t seq_num,
+               uint16_t app_idx, uint8_t *data, uint16_t len)
+{
+       GList *l;
+       bool res;
+       uint64_t iv_seq;
+       uint64_t iv_seq_remote;
+       uint8_t ele_idx;
+       struct mesh_element *element;
+       struct mesh_node *remote;
+       bool loopback;
+
+       if (!local_node || seq_num > 0xffffff)
+               return;
+
+       iv_seq = iv_index << 24;
+       iv_seq |= seq_num;
+
+       remote = node_find_by_addr(src);
+
+       if (!remote) {
+               if (local_node->provisioner) {
+                       bt_shell_printf("Remote node unknown (%4.4x)\n", src);
+                       return;
+               }
+
+               remote = g_new0(struct mesh_node, 1);
+               if (!remote)
+                       return;
+
+               /* Not Provisioner; Assume all SRC elements stand alone */
+               remote->primary = src;
+               remote->num_ele = 1;
+               nodes = g_list_append(nodes, remote);
+       }
+
+       loopback = (src < (local_node->primary + local_node->num_ele) &&
+                                               src >= local_node->primary);
+
+       if (!loopback) {
+               iv_seq_remote = remote->iv_index << 24;
+               iv_seq |= remote->seq_number;
+
+               if (iv_seq_remote >= iv_seq) {
+                       bt_shell_printf("Replayed message detected "
+                                       "(%016" PRIx64 " >= %016" PRIx64 ")\n",
+                                                       iv_seq_remote, iv_seq);
+                       return;
+               }
+       }
+
+       if (IS_GROUP(dst) || IS_VIRTUAL(dst)) {
+               /* TODO: if subscription address, deliver to subscribers */
+               return;
+       }
+
+       if (IS_ALL_NODES(dst)) {
+               ele_idx = 0;
+       } else {
+               if (dst >= (local_node->primary + local_node->num_ele) ||
+                       dst < local_node->primary)
+                               return;
+
+               ele_idx = dst - local_node->primary;
+       }
+
+       l = g_list_find_custom(local_node->elements,
+                               GUINT_TO_POINTER(ele_idx), match_element_idx);
+
+       /* This should not happen */
+       if (!l)
+               return;
+
+       element = l->data;
+       res = deliver_model_data(element, src, app_idx, data, len);
+
+       if (res && !loopback) {
+               /* TODO: Save remote in Replay Protection db */
+               remote->iv_index = iv_index;
+               remote->seq_number = seq_num;
+               prov_db_node_set_iv_seq(remote, iv_index, seq_num);
+       }
+}
+
+static gboolean restore_model_state(gpointer data)
+{
+       struct mesh_model *model = data;
+       GList *l;
+       struct mesh_model_ops *ops;
+
+       ops = &model->cbs;
+
+       if (model->bindings && ops->bind) {
+               for (l = model->bindings; l; l = l->next) {
+                       if (ops->bind(GPOINTER_TO_UINT(l->data), ACTION_ADD) !=
+                               MESH_STATUS_SUCCESS)
+                               break;
+               }
+       }
+
+       if (model->pub && ops->pub)
+               ops->pub(model->pub);
+
+       g_idle_remove_by_data(data);
+
+       return true;
+
+}
+
+bool node_local_model_register(uint8_t ele_idx, uint16_t model_id,
+                               struct mesh_model_ops *ops, void *user_data)
+{
+       uint32_t id = 0xffff0000 | model_id;
+
+       return node_local_vendor_model_register(ele_idx, id, ops, user_data);
+}
+
+bool node_local_vendor_model_register(uint8_t ele_idx, uint32_t model_id,
+                               struct mesh_model_ops *ops, void *user_data)
+{
+       struct mesh_element *ele;
+       struct mesh_model *model;
+       GList *l;
+
+       if (!local_node)
+               return false;
+
+       l = g_list_find_custom(local_node->elements, GUINT_TO_POINTER(ele_idx),
+                               match_element_idx);
+       if (!l)
+               return false;
+
+       ele = l->data;
+
+       l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
+                               match_model_id);
+       if (!l)
+               return false;
+
+       model = l->data;
+       model->cbs = *ops;
+       model->user_data = user_data;
+
+       if (model_id >= 0xffff0000)
+               model_id = model_id & 0xffff;
+
+       /* Silently assign device key binding to configuration models */
+       if (model_id == CONFIG_SERVER_MODEL_ID ||
+                                       model_id == CONFIG_CLIENT_MODEL_ID) {
+               model->bindings = g_list_append(model->bindings,
+                                               GUINT_TO_POINTER(APP_IDX_DEV));
+       } else {
+               g_idle_add(restore_model_state, model);
+       }
+
+       return true;
+}
+
+bool node_set_element(struct mesh_node *node, uint8_t ele_idx)
+{
+       struct mesh_element *ele;
+       GList *l;
+
+       if (!node)
+               return false;
+
+       l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+                               match_element_idx);
+       if (l)
+               return false;
+
+       ele = g_malloc0(sizeof(struct mesh_element));
+       if (!ele)
+               return false;
+
+       ele->index = ele_idx;
+       node->elements = g_list_append(node->elements, ele);
+
+       return true;
+}
+
+bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id)
+{
+       struct mesh_element *ele;
+       struct mesh_model *model;
+       GList *l;
+
+       if (!node)
+               return false;
+
+       l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+                               match_element_idx);
+       if (!l)
+               return false;
+
+       ele = l->data;
+
+       l = g_list_find_custom(ele->models, GUINT_TO_POINTER(id),
+                               match_model_id);
+       if (l)
+               return false;
+
+       model = g_malloc0(sizeof(struct mesh_model));
+       if (!model)
+               return false;
+
+       model->id = id;
+       ele->models = g_list_append(ele->models, model);
+
+       return true;
+}
+
+bool node_set_composition(struct mesh_node *node,
+                               struct mesh_node_composition *comp)
+{
+       if (!node || !comp || node->comp)
+               return false;
+
+       node->comp = g_malloc0(sizeof(struct mesh_node_composition));
+       if (!node->comp)
+               return false;
+
+       *(node->comp) = *comp;
+       return true;
+}
+
+struct mesh_node_composition *node_get_composition(struct mesh_node *node)
+{
+       if (!node)
+               return NULL;
+
+       return node->comp;
+}
+
+static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
+                                                       uint32_t model_id)
+{
+       struct mesh_element *ele;
+       GList *l;
+
+       if (!node)
+               return NULL;
+
+       l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
+                               match_element_idx);
+       if (!l)
+               return NULL;
+
+       ele = l->data;
+
+       l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
+                               match_model_id);
+       if (!l)
+               return NULL;
+
+       return l->data;
+
+}
+
+bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
+                       uint32_t model_id, uint16_t app_idx)
+{
+       struct mesh_model *model;
+       GList *l;
+
+       model = get_model(node, ele_idx, model_id);
+       if(!model)
+               return false;
+
+       l = g_list_find(model->bindings, GUINT_TO_POINTER(app_idx));
+       if (l)
+               return false;
+
+       if ((node == local_node) && model->cbs.bind) {
+               if (!model->cbs.bind(app_idx, ACTION_ADD))
+                       return false;
+       }
+
+       model->bindings = g_list_append(model->bindings,
+                                       GUINT_TO_POINTER(app_idx));
+
+       return true;
+}
+
+uint8_t node_get_default_ttl(struct mesh_node *node)
+{
+       if (!node)
+               return DEFAULT_TTL;
+       else if (node == local_node)
+               return net_get_default_ttl();
+       else
+               return node->ttl;
+}
+
+bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl)
+{
+       if (!node)
+               return false;
+
+       node->ttl = ttl;
+
+       if (node == local_node || local_node == NULL)
+               return net_set_default_ttl(ttl);
+
+       return true;
+}
+
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
+{
+       if (!node)
+               return false;
+
+       node->seq_number = seq;
+
+       if (node == local_node || local_node == NULL)
+               return net_set_seq_num(seq);
+
+       return true;
+}
+
+uint32_t node_get_sequence_number(struct mesh_node *node)
+{
+       if (!node)
+               return 0xffffffff;
+       else if (node == local_node)
+               return net_get_seq_num();
+
+       return node->seq_number;
+}
+
+bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index)
+{
+       if (!node)
+               return false;
+
+       node->iv_index = iv_index;
+       return true;
+}
+
+uint32_t node_get_iv_index(struct mesh_node *node)
+{
+       bool update;
+
+       if (!node)
+               return 0xffffffff;
+       else if (node == local_node)
+               return net_get_iv_index(&update);
+       return node->iv_index;
+}
+
+bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,
+                                               struct mesh_publication *pub)
+{
+       struct mesh_model *model;
+
+       model = get_model(node, ele, model_id);
+       if(!model)
+               return false;
+
+       if (!model->pub)
+               model->pub = g_malloc0(sizeof(struct mesh_publication));
+       if (!model)
+               return false;
+
+       memcpy(model->pub, pub, (sizeof(struct mesh_publication)));
+
+       if((node == local_node) && model->cbs.pub)
+               model->cbs.pub(pub);
+       return true;
+}
+
+struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,
+                                                       uint32_t model_id)
+{
+       struct mesh_model *model;
+
+       model = get_model(node, ele, model_id);
+       if(!model)
+               return NULL;
+       else
+               return model->pub;
+}
diff --git a/mesh/node.h b/mesh/node.h
new file mode 100644 (file)
index 0000000..1fab80a
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+struct mesh_node;
+
+#define ACTION_ADD             1
+#define ACTION_UPDATE          2
+#define ACTION_DELETE          3
+
+struct prov_svc_data {
+       uint16_t oob;
+       uint8_t dev_uuid[16];
+};
+
+struct mesh_node_composition {
+       bool relay;
+       bool proxy;
+       bool lpn;
+       bool friend;
+       uint16_t cid;
+       uint16_t pid;
+       uint16_t vid;
+       uint16_t crpl;
+};
+
+struct mesh_publication {
+       uint16_t app_idx;
+       union {
+               uint16_t addr16;
+               uint8_t va_128[16];
+       } u;
+       uint8_t ttl;
+       uint8_t credential;
+       uint8_t period;
+       uint8_t retransmit;
+};
+
+typedef bool (*node_model_recv_callback)(uint16_t src, uint8_t *data,
+                                               uint16_t len, void *user_data);
+typedef int (*node_model_bind_callback)(uint16_t app_idx, int action);
+typedef void (*node_model_pub_callback)(struct mesh_publication *pub);
+typedef void (*node_model_sub_callback)(uint16_t sub_addr, int action);
+
+struct mesh_model_ops {
+       node_model_recv_callback recv;
+       node_model_bind_callback bind;
+       node_model_pub_callback pub;
+       node_model_sub_callback sub;
+};
+
+struct mesh_node *node_find_by_addr(uint16_t addr);
+struct mesh_node *node_find_by_uuid(uint8_t uuid[16]);
+struct mesh_node *node_create_new(struct prov_svc_data *prov);
+struct mesh_node *node_new(void);
+void node_free(struct mesh_node *node);
+bool node_is_provisioned(struct mesh_node *node);
+void *node_get_prov(struct mesh_node *node);
+void node_set_prov(struct mesh_node *node, void *prov);
+bool node_app_key_add(struct mesh_node *node, uint16_t idx);
+bool node_net_key_add(struct mesh_node *node, uint16_t index);
+bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
+                               uint16_t idx);
+bool node_net_key_delete(struct mesh_node *node, uint16_t index);
+void node_set_primary(struct mesh_node *node, uint16_t unicast);
+uint16_t node_get_primary(struct mesh_node *node);
+uint16_t node_get_primary_net_idx(struct mesh_node *node);
+void node_set_device_key(struct mesh_node *node, uint8_t *key);
+uint8_t *node_get_device_key(struct mesh_node *node);
+void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
+uint8_t node_get_num_elements(struct mesh_node *node);
+bool node_parse_composition(struct mesh_node *node, uint8_t *buf, uint16_t len);
+GList *node_get_net_keys(struct mesh_node *node);
+GList *node_get_app_keys(struct mesh_node *node);
+void node_cleanup(void);
+
+bool node_set_local_node(struct mesh_node *node);
+struct mesh_node *node_get_local_node(void);
+void node_local_data_handler(uint16_t src, uint32_t dst,
+                               uint32_t iv_index, uint32_t seq_num,
+                               uint16_t app_idx, uint8_t *data, uint16_t len);
+
+bool node_local_model_register(uint8_t element_idx, uint16_t model_id,
+                               struct mesh_model_ops *ops, void *user_data);
+bool node_local_vendor_model_register(uint8_t element_idx, uint32_t model_id,
+                               struct mesh_model_ops *ops, void *user_data);
+
+bool node_set_element(struct mesh_node *node, uint8_t ele_idx);
+bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id);
+struct mesh_node_composition *node_get_composition(struct mesh_node *node);
+bool node_set_composition(struct mesh_node *node,
+                               struct mesh_node_composition *comp);
+bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
+                       uint32_t model_id, uint16_t app_idx);
+uint8_t node_get_default_ttl(struct mesh_node *node);
+bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl);
+bool node_set_sequence_number(struct mesh_node *node, uint32_t seq);
+uint32_t node_get_sequence_number(struct mesh_node *node);
+bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index);
+uint32_t node_get_iv_index(struct mesh_node *node);
+bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,
+                                               struct mesh_publication *pub);
+struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,
+                                                       uint32_t model_id);
diff --git a/mesh/onoff-model.c b/mesh/onoff-model.c
new file mode 100644 (file)
index 0000000..dbbe697
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/prov-db.h"
+#include "mesh/util.h"
+#include "mesh/onoff-model.h"
+
+static uint8_t trans_id;
+static uint16_t onoff_app_idx = APP_IDX_INVALID;
+
+static int client_bind(uint16_t app_idx, int action)
+{
+       if (action == ACTION_ADD) {
+               if (onoff_app_idx != APP_IDX_INVALID) {
+                       return MESH_STATUS_INSUFF_RESOURCES;
+               } else {
+                       onoff_app_idx = app_idx;
+                       bt_shell_printf("On/Off client model: new binding %4.4x\n",
+                                                               app_idx);
+               }
+       } else {
+               if (onoff_app_idx == app_idx)
+                       onoff_app_idx = APP_IDX_INVALID;
+       }
+       return MESH_STATUS_SUCCESS;
+}
+
+static void print_remaining_time(uint8_t remaining_time)
+{
+       uint8_t step = (remaining_time & 0xc0) >> 6;
+       uint8_t count = remaining_time & 0x3f;
+       int secs = 0, msecs = 0, minutes = 0, hours = 0;
+
+       switch (step) {
+       case 0:
+               msecs = 100 * count;
+               secs = msecs / 1000;
+               msecs -= (secs * 1000);
+               break;
+       case 1:
+               secs = 1 * count;
+               minutes = secs / 60;
+               secs -= (minutes * 60);
+               break;
+
+       case 2:
+               secs = 10 * count;
+               minutes = secs / 60;
+               secs -= (minutes * 60);
+               break;
+       case 3:
+               minutes = 10 * count;
+               hours = minutes / 60;
+               minutes -= (hours * 60);
+               break;
+
+       default:
+               break;
+       }
+
+       bt_shell_printf("\n\t\tRemaining time: %d hrs %d mins %d secs %d msecs\n",
+                                               hours, minutes, secs, msecs);
+
+}
+
+static bool client_msg_recvd(uint16_t src, uint8_t *data,
+                               uint16_t len, void *user_data)
+{
+       uint32_t opcode;
+       int n;
+
+       if (mesh_opcode_get(data, len, &opcode, &n)) {
+               len -= n;
+               data += n;
+       } else
+               return false;
+
+       bt_shell_printf("On Off Model Message received (%d) opcode %x\n",
+                                                               len, opcode);
+       print_byte_array("\t",data, len);
+
+       switch (opcode & ~OP_UNRELIABLE) {
+       default:
+               return false;
+
+       case OP_GENERIC_ONOFF_STATUS:
+               if (len != 1 && len != 3)
+                       break;
+
+               bt_shell_printf("Node %4.4x: Off Status present = %s",
+                                               src, data[0] ? "ON" : "OFF");
+
+               if (len == 3) {
+                       bt_shell_printf(", target = %s", data[1] ? "ON" : "OFF");
+                       print_remaining_time(data[2]);
+               } else
+                       bt_shell_printf("\n");
+               break;
+       }
+
+       return true;
+}
+
+
+static uint32_t target;
+static uint32_t parms[8];
+
+static uint32_t read_input_parameters(int argc, char *argv[])
+{
+       uint32_t i;
+
+       if (!argc)
+               return 0;
+
+       --argc;
+       ++argv;
+
+       if (!argc || argv[0][0] == '\0')
+               return 0;
+
+       memset(parms, 0xff, sizeof(parms));
+
+       for (i = 0; i < sizeof(parms)/sizeof(parms[0]) && i < (unsigned) argc;
+                                                                       i++) {
+               sscanf(argv[i], "%x", &parms[i]);
+               if (parms[i] == 0xffffffff)
+                       break;
+       }
+
+       return i;
+}
+
+static void cmd_set_node(int argc, char *argv[])
+{
+       uint32_t dst;
+       char *end;
+
+       dst = strtol(argv[1], &end, 16);
+       if (end != (argv[1] + 4)) {
+               bt_shell_printf("Bad unicast address %s: "
+                               "expected format 4 digit hex\n", argv[1]);
+               target = UNASSIGNED_ADDRESS;
+       } else {
+               bt_shell_printf("Controlling ON/OFF for node %4.4x\n", dst);
+               target = dst;
+               set_menu_prompt("on/off", argv[1]);
+       }
+}
+
+static bool send_cmd(uint8_t *buf, uint16_t len)
+{
+       struct mesh_node *node = node_get_local_node();
+       uint8_t ttl;
+
+       if(!node)
+               return false;
+
+       ttl = node_get_default_ttl(node);
+
+       return net_access_layer_send(ttl, node_get_primary(node),
+                                       target, onoff_app_idx, buf, len);
+}
+
+static void cmd_get_status(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       node = node_find_by_addr(target);
+
+       if (!node)
+               return;
+
+       n = mesh_opcode_set(OP_GENERIC_ONOFF_GET, msg);
+
+       if (!send_cmd(msg, n))
+               bt_shell_printf("Failed to send \"GENERIC ON/OFF GET\"\n");
+}
+
+static void cmd_set(int argc, char *argv[])
+{
+       uint16_t n;
+       uint8_t msg[32];
+       struct mesh_node *node;
+
+       if (IS_UNASSIGNED(target)) {
+               bt_shell_printf("Destination not set\n");
+               return;
+       }
+
+       node = node_find_by_addr(target);
+
+       if (!node)
+               return;
+
+       if ((read_input_parameters(argc, argv) != 1) &&
+                                       parms[0] != 0 && parms[0] != 1) {
+               bt_shell_printf("Bad arguments: Expecting \"0\" or \"1\"\n");
+               return;
+       }
+
+       n = mesh_opcode_set(OP_GENERIC_ONOFF_SET, msg);
+       msg[n++] = parms[0];
+       msg[n++] = trans_id++;
+
+       if (!send_cmd(msg, n))
+               bt_shell_printf("Failed to send \"GENERIC ON/OFF SET\"\n");
+
+}
+
+static const struct bt_shell_menu onoff_menu = {
+       .name = "onoff",
+       .desc = "On/Off Model Submenu",
+       .entries = {
+       {"target",              "<unicast>",                    cmd_set_node,
+                                               "Set node to configure"},
+       {"get",                 NULL,                           cmd_get_status,
+                                               "Get ON/OFF status"},
+       {"onoff",               "<0/1>",                        cmd_set,
+                                               "Send \"SET ON/OFF\" command"},
+       {} },
+};
+
+static struct mesh_model_ops client_cbs = {
+       client_msg_recvd,
+       client_bind,
+       NULL,
+       NULL
+};
+
+bool onoff_client_init(uint8_t ele)
+{
+       if (!node_local_model_register(ele, GENERIC_ONOFF_CLIENT_MODEL_ID,
+                                       &client_cbs, NULL))
+               return false;
+
+       bt_shell_add_submenu(&onoff_menu);
+
+       return true;
+}
old mode 100755 (executable)
new mode 100644 (file)
similarity index 61%
rename from android/dis.h
rename to mesh/onoff-model.h
index faf27b3..3159905
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2017  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
  *
  */
 
-struct bt_dis;
+#define GENERIC_ONOFF_SERVER_MODEL_ID  0x1000
+#define GENERIC_ONOFF_CLIENT_MODEL_ID  0x1001
 
-struct bt_dis *bt_dis_new(void *primary);
+#define OP_GENERIC_ONOFF_GET                   0x8201
+#define OP_GENERIC_ONOFF_SET                   0x8202
+#define OP_GENERIC_ONOFF_SET_UNACK             0x8203
+#define OP_GENERIC_ONOFF_STATUS                        0x8204
 
-struct bt_dis *bt_dis_ref(struct bt_dis *dis);
-void bt_dis_unref(struct bt_dis *dis);
-
-bool bt_dis_attach(struct bt_dis *dis, void *gatt);
-void bt_dis_detach(struct bt_dis *dis);
-
-typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor,
-                                       uint16_t product, uint16_t version,
-                                       void *user_data);
-
-bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
-                                                       void *user_data);
+void onoff_set_node(const char *args);
+bool onoff_client_init(uint8_t ele);
diff --git a/mesh/prov-db.c b/mesh/prov-db.c
new file mode 100644 (file)
index 0000000..8a7b47f
--- /dev/null
@@ -0,0 +1,1628 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <json-c/json.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/shell.h"
+
+#include "mesh/mesh-net.h"
+#include "mesh/crypto.h"
+#include "mesh/keys.h"
+#include "mesh/net.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+#include "mesh/prov-db.h"
+
+#define CHECK_KEY_IDX_RANGE(x) (((x) >= 0) && ((x) <= 4095))
+
+static const char *prov_filename;
+static const char *local_filename;
+
+static char* prov_file_read(const char *filename)
+{
+       int fd;
+       char *str;
+       struct stat st;
+       ssize_t sz;
+
+       if (!filename)
+               return NULL;
+
+       fd = open(filename,O_RDONLY);
+       if (!fd)
+               return NULL;
+
+       if (fstat(fd, &st) == -1) {
+               close(fd);
+               return NULL;
+       }
+
+       str = (char *) g_malloc0(st.st_size + 1);
+       if (!str) {
+               close(fd);
+               return NULL;
+       }
+
+       sz = read(fd, str, st.st_size);
+       if (sz != st.st_size)
+               bt_shell_printf("Incomplete read: %d vs %d\n", (int)sz,
+                                                       (int)(st.st_size));
+
+       close(fd);
+
+       return str;
+}
+
+static void prov_file_write(json_object *jmain, bool local)
+{
+       FILE *outfile;
+       const char *out_str;
+       const char *out_filename;
+
+       if (local)
+               out_filename = local_filename;
+       else
+               out_filename = prov_filename;
+
+       outfile = fopen(out_filename, "wr");
+       if (!outfile) {
+               bt_shell_printf("Failed to open file %s for writing\n", out_filename);
+               return;
+       }
+
+       out_str = json_object_to_json_string_ext(jmain,
+                                               JSON_C_TO_STRING_PRETTY);
+
+       fwrite(out_str, sizeof(char), strlen(out_str), outfile);
+       fclose(outfile);
+}
+
+static void put_uint16(json_object *jobject, const char *desc, uint16_t value)
+{
+       json_object *jstring;
+       char buf[5];
+
+       snprintf(buf, 5, "%4.4x", value);
+       jstring = json_object_new_string(buf);
+       json_object_object_add(jobject, desc, jstring);
+}
+
+static void put_uint32(json_object *jobject, const char *desc, uint32_t value)
+{
+       json_object *jstring;
+       char buf[9];
+
+       snprintf(buf, 9, "%8.8x", value);
+       jstring = json_object_new_string(buf);
+       json_object_object_add(jobject, desc, jstring);
+}
+
+static void put_uint16_array_entry(json_object *jarray, uint16_t value)
+{
+       json_object *jstring;
+       char buf[5];
+
+       snprintf(buf, 5, "%4.4x", value);
+       jstring = json_object_new_string(buf);
+       json_object_array_add(jarray, jstring);
+}
+
+static void put_uint32_array_entry(json_object *jarray, uint32_t value)
+{
+       json_object *jstring;
+       char buf[9];
+
+       snprintf(buf, 9, "%8.8x", value);
+       jstring = json_object_new_string(buf);
+       json_object_array_add(jarray, jstring);
+}
+
+static void put_uint16_list(json_object *jarray, GList *list)
+{
+       GList *l;
+
+       if (!list)
+               return;
+
+       for (l = list; l; l = l->next) {
+               uint32_t ivalue = GPOINTER_TO_UINT(l->data);
+               put_uint16_array_entry(jarray, ivalue);
+       }
+}
+
+static void add_node_idxs(json_object *jnode, const char *desc,
+                               GList *idxs)
+{
+       json_object *jarray;
+
+       jarray = json_object_new_array();
+
+       put_uint16_list(jarray, idxs);
+
+       json_object_object_add(jnode, desc, jarray);
+}
+
+static bool parse_unicast_range(json_object *jobject)
+{
+       int cnt;
+       int i;
+
+       cnt = json_object_array_length(jobject);
+
+       for (i = 0; i < cnt; ++i) {
+               json_object *jrange;
+               json_object *jvalue;
+               uint16_t low, high;
+               char *str;
+
+               jrange = json_object_array_get_idx(jobject, i);
+               json_object_object_get_ex(jrange, "lowAddress", &jvalue);
+               str = (char *)json_object_get_string(jvalue);
+               if (sscanf(str, "%04hx", &low) != 1)
+                       return false;
+
+               json_object_object_get_ex(jrange, "highAddress", &jvalue);
+               str = (char *)json_object_get_string(jvalue);
+               if (sscanf(str, "%04hx", &high) != 1)
+                       return false;
+
+               if(high < low)
+                       return false;
+
+               net_add_address_pool(low, high);
+       }
+       return true;
+}
+
+static int parse_node_keys(struct mesh_node *node, json_object *jidxs,
+                               bool is_app_key)
+{
+       int idx_cnt;
+       int i;
+
+       idx_cnt = json_object_array_length(jidxs);
+       for (i = 0; i < idx_cnt; ++i) {
+               int idx;
+               json_object *jvalue;
+
+               jvalue = json_object_array_get_idx(jidxs, i);
+               if (!jvalue)
+                       break;
+               idx = json_object_get_int(jvalue);
+               if (!CHECK_KEY_IDX_RANGE(idx))
+                       break;
+
+               if (is_app_key)
+                       node_app_key_add(node, idx);
+               else
+                       node_net_key_add(node, idx);
+       }
+
+       return i;
+}
+
+static bool parse_composition_models(struct mesh_node *node, int index,
+                                       json_object *jmodels)
+{
+       int model_cnt;
+       int i;
+
+       model_cnt = json_object_array_length(jmodels);
+
+       for (i = 0; i < model_cnt; ++i) {
+               json_object *jmodel;
+               char *str;
+               uint32_t model_id;
+               int len;
+
+               jmodel = json_object_array_get_idx(jmodels, i);
+               str = (char *)json_object_get_string(jmodel);
+               len = strlen(str);
+
+               if (len != 4 && len != 8)
+                       return false;
+
+               if (sscanf(str, "%08x", &model_id) != 1)
+                       return false;
+               if (len == 4)
+                       model_id += 0xffff0000;
+
+               node_set_model(node, index, model_id);
+       }
+
+       return true;
+}
+
+static bool parse_composition_elements(struct mesh_node *node,
+                                       json_object *jelements)
+{
+       int el_cnt;
+       int i;
+
+       el_cnt = json_object_array_length(jelements);
+       node_set_num_elements(node, el_cnt);
+
+       for (i = 0; i < el_cnt; ++i) {
+               json_object *jelement;
+               json_object *jmodels;
+               json_object *jvalue;
+               int index;
+
+               jelement = json_object_array_get_idx(jelements, i);
+               json_object_object_get_ex(jelement, "elementIndex", &jvalue);
+               if (jvalue) {
+                       index = json_object_get_int(jvalue);
+                       if (index >= el_cnt) {
+                               return false;
+                       }
+               } else
+                       return false;
+
+               if (!node_set_element(node, index))
+                       return false;
+
+               json_object_object_get_ex(jelement, "models", &jmodels);
+               if (!jmodels)
+                       continue;
+
+               if(!parse_composition_models(node, index, jmodels))
+                       return false;
+       }
+       return true;
+}
+
+static bool parse_model_pub(struct mesh_node *node, int ele_idx,
+                               uint32_t model_id, json_object *jpub)
+{
+       json_object *jvalue;
+       struct mesh_publication pub;
+       char *str;
+
+       memset(&pub, 0, sizeof(struct mesh_publication));
+
+       /* Read only required fields */
+       json_object_object_get_ex(jpub, "address", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+       if (sscanf(str, "%04hx", &pub.u.addr16) != 1)
+               return false;
+
+       json_object_object_get_ex(jpub, "index", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+       if (sscanf(str, "%04hx", &pub.app_idx) != 1)
+               return false;
+
+
+       json_object_object_get_ex(jpub, "ttl", &jvalue);
+       pub.ttl = json_object_get_int(jvalue);
+
+       if (!node_model_pub_set(node, ele_idx, model_id, &pub))
+                       return false;
+
+       return true;
+}
+
+static bool parse_bindings(struct mesh_node *node, int ele_idx,
+                               uint32_t model_id, json_object *jbindings)
+{
+       int cnt;
+       int i;
+
+       cnt = json_object_array_length(jbindings);
+
+       for (i = 0; i < cnt; ++i) {
+               int key_idx;
+               json_object *jvalue;
+
+               jvalue = json_object_array_get_idx(jbindings, i);
+               if (!jvalue)
+                       return true;
+
+               key_idx = json_object_get_int(jvalue);
+               if (!CHECK_KEY_IDX_RANGE(key_idx))
+                       return false;
+
+               if (!node_add_binding(node, ele_idx, model_id, key_idx))
+                       return false;
+       }
+
+       return true;
+}
+
+static json_object* find_configured_model(struct mesh_node *node, int ele_idx,
+                                      json_object *jmodels, uint32_t target_id)
+{
+       int model_cnt;
+       int i;
+
+       model_cnt = json_object_array_length(jmodels);
+
+       for (i = 0; i < model_cnt; ++i) {
+               json_object *jmodel;
+               json_object *jvalue;
+               char *str;
+               int len;
+               uint32_t model_id;
+
+               jmodel = json_object_array_get_idx(jmodels, i);
+
+               json_object_object_get_ex(jmodel, "modelId", &jvalue);
+               str = (char *)json_object_get_string(jvalue);
+
+               len = strlen(str);
+
+               if (len != 4 && len != 8)
+                       return NULL;
+
+               if (sscanf(str, "%08x", &model_id) != 1)
+                       return NULL;
+
+               if (len == 4)
+                       model_id += 0xffff0000;
+
+               if (model_id == target_id)
+                       return jmodel;
+       }
+
+       return NULL;
+}
+
+static bool parse_configuration_models(struct mesh_node *node, int ele_idx,
+                                                       json_object *jmodels)
+{
+       int model_cnt;
+       int i;
+
+       model_cnt = json_object_array_length(jmodels);
+
+       for (i = 0; i < model_cnt; ++i) {
+               json_object *jmodel;
+               json_object *jvalue;
+               json_object *jarray;
+               char *str;
+               int len;
+               uint32_t model_id;
+
+               jmodel = json_object_array_get_idx(jmodels, i);
+
+               json_object_object_get_ex(jmodel, "modelId", &jvalue);
+               str = (char *)json_object_get_string(jvalue);
+
+               len = strlen(str);
+
+               if (len != 4 && len != 8)
+                       return false;
+
+               if (sscanf(str, "%08x", &model_id) != 1)
+                       return false;
+               if (len == 4)
+                       model_id += 0xffff0000;
+
+               json_object_object_get_ex(jmodel, "bind", &jarray);
+               if (jarray && !parse_bindings(node, ele_idx, model_id, jarray))
+                       return false;
+
+               json_object_object_get_ex(jmodel, "publish", &jvalue);
+
+               if (jvalue && !parse_model_pub(node, ele_idx, model_id, jvalue))
+                       return false;
+       }
+
+       return true;
+}
+
+static bool parse_configuration_elements(struct mesh_node *node,
+                               json_object *jelements, bool local)
+{
+       int el_cnt;
+       int i;
+
+       el_cnt = json_object_array_length(jelements);
+       node_set_num_elements(node, el_cnt);
+
+       for (i = 0; i < el_cnt; ++i) {
+               json_object *jelement;
+               json_object *jmodels;
+               json_object *jvalue;
+               int index;
+               uint16_t addr;
+
+               jelement = json_object_array_get_idx(jelements, i);
+               json_object_object_get_ex(jelement, "elementIndex", &jvalue);
+               if (jvalue) {
+                       index = json_object_get_int(jvalue);
+                       if (index >= el_cnt) {
+                               return false;
+                       }
+               } else
+                       return false;
+
+               if (index == 0) {
+                       char *str;
+
+                       json_object_object_get_ex(jelement, "unicastAddress",
+                                                       &jvalue);
+                       str = (char *)json_object_get_string(jvalue);
+                       if (sscanf(str, "%04hx", &addr) != 1)
+                               return false;
+
+                       if (!local && !net_reserve_address_range(addr, el_cnt))
+                               return false;
+
+                       node_set_primary(node, addr);
+               }
+
+               json_object_object_get_ex(jelement, "models", &jmodels);
+               if (!jmodels)
+                       continue;
+
+               if(!parse_configuration_models(node, index, jmodels))
+                       return false;
+       }
+       return true;
+}
+
+static void add_key(json_object *jobject, const char *desc, uint8_t* key)
+{
+       json_object *jstring;
+       char hexstr[33];
+
+       hex2str(key, 16, hexstr, 33);
+       jstring = json_object_new_string(hexstr);
+       json_object_object_add(jobject, desc, jstring);
+}
+
+static json_object *find_node_by_primary(json_object *jmain, uint16_t primary)
+{
+       json_object *jarray;
+       int i, len;
+
+       json_object_object_get_ex(jmain, "nodes", &jarray);
+
+       if (!jarray)
+               return NULL;
+       len = json_object_array_length(jarray);
+
+       for (i = 0; i < len; ++i) {
+               json_object *jnode;
+               json_object *jconfig;
+               json_object *jelements;
+               json_object *jelement;
+               json_object *jvalue;
+               char *str;
+               uint16_t addr;
+
+               jnode = json_object_array_get_idx(jarray, i);
+               if (!jnode)
+                       return NULL;
+
+               json_object_object_get_ex(jnode, "configuration", &jconfig);
+               if (!jconfig)
+                       return NULL;
+
+               json_object_object_get_ex(jconfig, "elements", &jelements);
+               if (!jelements)
+                       return NULL;
+
+               jelement = json_object_array_get_idx(jelements, 0);
+               if (!jelement)
+                       return NULL;
+
+               json_object_object_get_ex(jelement, "unicastAddress",
+                                                               &jvalue);
+               str = (char *)json_object_get_string(jvalue);
+               if (sscanf(str, "%04hx", &addr) != 1)
+                               return NULL;
+
+               if (addr == primary)
+                       return jnode;
+       }
+
+       return NULL;
+
+}
+
+void prov_db_print_node_composition(struct mesh_node *node)
+{
+       char *in_str;
+       const char *comp_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jcomp;
+       uint16_t primary = node_get_primary(node);
+       const char *filename;
+       bool res = false;
+
+       if (!node || !node_get_composition(node))
+               return;
+
+       if (node == node_get_local_node())
+               filename = local_filename;
+       else
+               filename = prov_filename;
+
+       in_str = prov_file_read(filename);
+       if (!in_str)
+               return;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       jnode = find_node_by_primary(jmain, primary);
+       if (!jnode)
+               goto done;
+
+       json_object_object_get_ex(jnode, "composition", &jcomp);
+       if (!jcomp)
+               goto done;
+
+       comp_str = json_object_to_json_string_ext(jcomp,
+                                               JSON_C_TO_STRING_PRETTY);
+
+       res = true;
+
+done:
+       if (res)
+               bt_shell_printf("\tComposition data for node %4.4x %s\n",
+                                                       primary, comp_str);
+       else
+               bt_shell_printf("\tComposition data for node %4.4x not present\n",
+                                                               primary);
+       g_free(in_str);
+
+       if (jmain)
+               json_object_put(jmain);
+}
+
+bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
+                                                               uint16_t len)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jcomp;
+       json_object *jbool;
+       json_object *jfeatures;
+       json_object *jelements;
+       struct mesh_node_composition *comp;
+       uint8_t num_ele;
+       int i;
+       uint16_t primary = node_get_primary(node);
+       bool res = NULL;
+
+       comp = node_get_composition(node);
+       if (!comp)
+               return false;
+
+       in_str = prov_file_read(prov_filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       jnode = find_node_by_primary(jmain, primary);
+       if (!jnode)
+               goto done;
+
+       jcomp = json_object_new_object();
+
+       put_uint16(jcomp, "cid", comp->cid);
+       put_uint16(jcomp, "pid", comp->pid);
+       put_uint16(jcomp, "vid", comp->pid);
+       put_uint16(jcomp, "crpl", comp->crpl);
+
+       jfeatures = json_object_new_object();
+       jbool = json_object_new_boolean(comp->relay);
+       json_object_object_add(jfeatures, "relay", jbool);
+       jbool = json_object_new_boolean(comp->proxy);
+       json_object_object_add(jfeatures, "proxy", jbool);
+       jbool = json_object_new_boolean(comp->friend);
+       json_object_object_add(jfeatures, "friend", jbool);
+       jbool = json_object_new_boolean(comp->lpn);
+       json_object_object_add(jfeatures, "lpn", jbool);
+       json_object_object_add(jcomp, "features", jfeatures);
+
+       data += 11;
+       len -= 11;
+
+       num_ele =  node_get_num_elements(node);
+
+       jelements = json_object_new_array();
+
+       for (i = 0; i < num_ele; ++i) {
+               json_object *jelement;
+               json_object *jmodels;
+               json_object *jint;
+               uint32_t mod_id;
+               uint16_t vendor_id;
+               uint8_t m, v;
+
+               jelement = json_object_new_object();
+
+               /* Element Index */
+               jint = json_object_new_int(i);
+               json_object_object_add(jelement, "elementIndex", jint);
+
+               /* Location */
+               put_uint16(jelement, "location", get_le16(data));
+               data += 2;
+               m = *data++;
+               v = *data++;
+               len -= 4;
+
+               /* Models */
+               jmodels = json_object_new_array();
+               while (len >= 2 && m--) {
+                       mod_id = get_le16(data);
+                       data += 2;
+                       len -= 2;
+                       put_uint16_array_entry(jmodels, (uint16_t) mod_id);
+               }
+
+               while (len >= 4 && v--) {
+                       mod_id = get_le16(data + 2);
+                       vendor_id = get_le16(data);
+                       mod_id |= (vendor_id << 16);
+                       data += 4;
+                       len -= 4;
+                       put_uint32_array_entry(jmodels, mod_id);
+               }
+
+               json_object_object_add(jelement, "models", jmodels);
+               json_object_array_add(jelements, jelement);
+       }
+
+       json_object_object_add(jcomp, "elements", jelements);
+
+       json_object_object_add(jnode, "composition", jcomp);
+
+       prov_file_write(jmain, false);
+
+       res = true;;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+}
+
+bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jconfig;
+       json_object *jvalue;
+       uint16_t primary = node_get_primary(node);
+       const char *filename;
+       bool local = node == node_get_local_node();
+       bool res = false;
+
+       if (local)
+               filename = local_filename;
+       else
+               filename = prov_filename;
+
+       in_str = prov_file_read(filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       if (local)
+               json_object_object_get_ex(jmain, "node", &jnode);
+       else
+               jnode = find_node_by_primary(jmain, primary);
+
+       if (!jnode)
+               goto done;
+
+       json_object_object_get_ex(jnode, "configuration", &jconfig);
+       if (!jconfig)
+               goto done;
+
+       json_object_object_del(jconfig, "defaultTTL");
+
+       jvalue = json_object_new_int(ttl);
+       json_object_object_add(jconfig, "defaultTTL", jvalue);
+
+       prov_file_write(jmain, local);
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+
+}
+
+static void set_local_iv_index(json_object *jobj, uint32_t idx, bool update)
+{
+       json_object *jvalue;
+
+       json_object_object_del(jobj, "IVindex");
+       jvalue = json_object_new_int(idx);
+       json_object_object_add(jobj, "IVindex", jvalue);
+
+       json_object_object_del(jobj, "IVupdate");
+       jvalue = json_object_new_int((update) ? 1 : 0);
+       json_object_object_add(jobj, "IVupdate", jvalue);
+
+}
+
+bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       bool res = false;
+
+       in_str = prov_file_read(local_filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       json_object_object_get_ex(jmain, "node", &jnode);
+       set_local_iv_index(jnode, iv_index, update);
+       prov_file_write(jmain, true);
+
+       g_free(in_str);
+       json_object_put(jmain);
+
+       /* If provisioner, save to global DB as well */
+       if (prov) {
+               in_str = prov_file_read(prov_filename);
+               if (!in_str)
+                       return false;
+
+               jmain = json_tokener_parse(in_str);
+               if (!jmain)
+                       goto done;
+
+               set_local_iv_index(jmain, iv_index, update);
+               prov_file_write(jmain, false);
+       }
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+
+}
+
+bool prov_db_local_set_seq_num(uint32_t seq_num)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jvalue;
+       bool res = false;
+
+       in_str = prov_file_read(local_filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       json_object_object_get_ex(jmain, "node", &jnode);
+
+       json_object_object_del(jnode, "sequenceNumber");
+       jvalue = json_object_new_int(seq_num);
+       json_object_object_add(jnode, "sequenceNumber", jvalue);
+
+       prov_file_write(jmain, true);
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+}
+
+bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jvalue;
+       uint16_t primary = node_get_primary(node);
+       bool res = false;
+
+       in_str = prov_file_read(prov_filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       jnode = find_node_by_primary(jmain, primary);
+       if (!jnode)
+               goto done;
+
+       json_object_object_del(jnode, "IVindex");
+
+       jvalue = json_object_new_int(iv);
+       json_object_object_add(jnode, "IVindex", jvalue);
+
+       json_object_object_del(jnode, "sequenceNumber");
+
+       jvalue = json_object_new_int(seq);
+       json_object_object_add(jnode, "sequenceNumber", jvalue);
+
+       prov_file_write(jmain, false);
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+
+}
+
+bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jnode;
+       json_object *jconfig;
+       json_object *jidxs;
+       uint16_t primary = node_get_primary(node);
+       const char *filename;
+       bool local = (node == node_get_local_node());
+       bool res = false;
+
+       if (local)
+               filename = local_filename;
+       else
+               filename = prov_filename;
+
+       in_str = prov_file_read(filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+
+       jnode = find_node_by_primary(jmain, primary);
+       if (!jnode)
+               goto done;
+
+       json_object_object_get_ex(jnode, "configuration", &jconfig);
+       if (!jconfig)
+               goto done;
+
+       json_object_object_del(jconfig, desc);
+
+       if (idxs) {
+               jidxs = json_object_new_array();
+               put_uint16_list(jidxs, idxs);
+               json_object_object_add(jconfig, desc, jidxs);
+       }
+
+       prov_file_write(jmain, local);
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if(jmain)
+               json_object_put(jmain);
+
+       return res;
+
+}
+
+static json_object *get_jmodel_obj(struct mesh_node *node, uint8_t ele_idx,
+                                       uint32_t model_id, json_object **jmain)
+{
+       char *in_str;
+       json_object *jnode;
+       json_object *jconfig;
+       json_object *jelements, *jelement;
+       json_object *jmodels, *jmodel = NULL;
+       uint16_t primary = node_get_primary(node);
+       const char *filename;
+       bool local = (node == node_get_local_node());
+
+       if (local)
+               filename = local_filename;
+       else
+               filename = prov_filename;
+
+       in_str = prov_file_read(filename);
+       if (!in_str)
+               return NULL;
+
+       *jmain = json_tokener_parse(in_str);
+       if (!(*jmain))
+               goto done;
+
+       if (local)
+               json_object_object_get_ex(*jmain, "node", &jnode);
+       else
+               jnode = find_node_by_primary(*jmain, primary);
+
+       if (!jnode)
+               goto done;
+
+       /* Configuration is mandatory for nodes in provisioning database */
+       json_object_object_get_ex(jnode, "configuration", &jconfig);
+       if (!jconfig)
+               goto done;
+
+       json_object_object_get_ex(jconfig, "elements", &jelements);
+       if (!jelements) {
+               goto done;
+       }
+
+       jelement = json_object_array_get_idx(jelements, ele_idx);
+       if (!jelement) {
+               goto done;
+       }
+
+       json_object_object_get_ex(jelement, "models", &jmodels);
+
+       if (!jmodels)  {
+               jmodels = json_object_new_array();
+               json_object_object_add(jelement, "models", jmodels);
+       } else {
+               jmodel = find_configured_model(node, ele_idx, jmodels,
+                                                               model_id);
+       }
+
+       if (!jmodel) {
+               jmodel = json_object_new_object();
+
+               if ((model_id & 0xffff0000) == 0xffff0000)
+                       put_uint16(jmodel, "modelId", model_id & 0xffff);
+               else
+                       put_uint32(jmodel, "modelId", model_id);
+
+               json_object_array_add(jmodels, jmodel);
+       }
+
+done:
+
+       g_free(in_str);
+
+       if(!jmodel && *jmain)
+               json_object_put(*jmain);
+
+       return jmodel;
+
+}
+
+bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
+                       uint32_t model_id, uint16_t app_idx)
+{
+       json_object *jmain;
+       json_object *jmodel;
+       json_object *jvalue;
+       json_object *jbindings = NULL;
+       bool local = (node == node_get_local_node());
+
+       jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
+
+       if (!jmodel)
+               return false;
+
+       json_object_object_get_ex(jmodel, "bind", &jbindings);
+
+       if (!jbindings) {
+               jbindings = json_object_new_array();
+               json_object_object_add(jmodel, "bind", jbindings);
+       }
+
+       jvalue = json_object_new_int(app_idx);
+       json_object_array_add(jbindings, jvalue);
+
+       prov_file_write(jmain, local);
+
+       json_object_put(jmain);
+
+       return true;
+}
+
+bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
+                                                       uint32_t model_id,
+                                               struct mesh_publication *pub)
+{
+       json_object *jmain;
+       json_object *jmodel;
+       json_object *jpub;
+       json_object *jvalue;
+       bool local = (node == node_get_local_node());
+
+       jmodel = get_jmodel_obj(node, ele_idx, model_id, &jmain);
+
+       if (!jmodel)
+               return false;
+
+       json_object_object_del(jmodel, "publish");
+       if (!pub)
+               goto done;
+
+       jpub = json_object_new_object();
+
+       /* Save only required fields */
+       put_uint16(jpub, "address", pub->u.addr16);
+       put_uint16(jpub, "index", pub->app_idx);
+       jvalue = json_object_new_int(pub->ttl);
+       json_object_object_add(jpub, "ttl", jvalue);
+
+       json_object_object_add(jmodel, "publish", jpub);
+
+done:
+       prov_file_write(jmain, local);
+
+       json_object_put(jmain);
+
+       return true;
+}
+
+bool prov_db_add_new_node(struct mesh_node *node)
+{
+       char *in_str;
+       json_object *jmain;
+       json_object *jarray;
+       json_object *jnode;
+       json_object *jconfig;
+       json_object *jelements;
+       uint8_t num_ele;
+       uint16_t primary;
+       int i;
+       bool first_node;
+       bool res = false;
+
+       in_str = prov_file_read(prov_filename);
+       if (!in_str)
+               return false;
+
+       jmain = json_tokener_parse(in_str);
+       if (!jmain)
+               goto done;
+       json_object_object_get_ex(jmain, "nodes", &jarray);
+
+       if (!jarray) {
+               jarray = json_object_new_array();
+               first_node = true;
+       } else
+               first_node = false;
+
+       jnode = json_object_new_object();
+
+       /* Device key */
+       add_key(jnode, "deviceKey", node_get_device_key(node));
+
+       /* Net key */
+       jconfig = json_object_new_object();
+       add_node_idxs(jconfig, "netKeys", node_get_net_keys(node));
+
+       num_ele = node_get_num_elements(node);
+       if (num_ele == 0)
+               goto done;
+
+       jelements = json_object_new_array();
+
+       primary = node_get_primary(node);
+       if (IS_UNASSIGNED(primary))
+               goto done;
+
+       for (i = 0; i < num_ele; ++i) {
+               json_object *jelement;
+               json_object *jint;
+
+               jelement = json_object_new_object();
+
+               /* Element Index */
+               jint = json_object_new_int(i);
+               json_object_object_add(jelement, "elementIndex", jint);
+
+               /* Unicast */
+               put_uint16(jelement, "unicastAddress", primary + i);
+
+               json_object_array_add(jelements, jelement);
+       }
+
+       json_object_object_add(jconfig, "elements", jelements);
+
+       json_object_object_add(jnode, "configuration", jconfig);
+
+       json_object_array_add(jarray, jnode);
+
+       if (first_node)
+               json_object_object_add(jmain, "nodes", jarray);
+
+       prov_file_write(jmain, false);
+
+       res = true;
+done:
+
+       g_free(in_str);
+
+       if (jmain)
+               json_object_put(jmain);
+
+       return res;
+}
+
+static bool parse_node_composition(struct mesh_node *node, json_object *jcomp)
+{
+       json_object *jvalue;
+       json_object *jelements;
+       json_bool enable;
+       char *str;
+       struct mesh_node_composition comp;
+
+       json_object_object_get_ex(jcomp, "cid", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+
+       if (sscanf(str, "%04hx", &comp.cid) != 1)
+               return false;
+
+       json_object_object_get_ex(jcomp, "pid", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+
+       if (sscanf(str, "%04hx", &comp.vid) != 1)
+               return false;
+
+       json_object_object_get_ex(jcomp, "vid", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+
+       if (sscanf(str, "%04hx", &comp.vid) != 1)
+               return false;
+
+       json_object_object_get_ex(jcomp, "crpl", &jvalue);
+       if (!jvalue)
+               return false;
+
+       str = (char *)json_object_get_string(jvalue);
+
+       if (sscanf(str, "%04hx", &comp.crpl) != 1)
+               return false;
+
+       /* Extract features */
+       json_object_object_get_ex(jcomp, "relay", &jvalue);
+       enable = json_object_get_boolean(jvalue);
+       comp.relay = (enable) ? true : false;
+
+       json_object_object_get_ex(jcomp, "proxy", &jvalue);
+       enable = json_object_get_boolean(jvalue);
+       comp.proxy = (enable) ? true : false;
+
+       json_object_object_get_ex(jcomp, "friend", &jvalue);
+       enable = json_object_get_boolean(jvalue);
+       comp.friend = (enable) ? true : false;
+
+       json_object_object_get_ex(jcomp, "lowPower", &jvalue);
+       enable = json_object_get_boolean(jvalue);
+       comp.lpn = (enable) ? true : false;
+
+       if (!node_set_composition(node, &comp))
+               return false;
+
+       json_object_object_get_ex(jcomp, "elements", &jelements);
+       if (!jelements)
+               return false;
+
+       return parse_composition_elements(node, jelements);
+}
+
+static bool parse_node(json_object *jnode, bool local)
+{
+       json_object *jconfig;
+       json_object *jelements;
+       json_object *jidxs;
+       json_object *jvalue;
+       json_object *jint;
+       uint8_t key[16];
+       char *value_str;
+       uint32_t idx;
+       struct mesh_node *node;
+
+       /* Device key */
+       if (!json_object_object_get_ex(jnode, "deviceKey", &jvalue) ||
+                                                               !jvalue) {
+               if (!mesh_get_random_bytes(key, 16))
+                       return false;
+
+               add_key(jnode, "deviceKey", key);
+       } else {
+               value_str = (char *)json_object_get_string(jvalue);
+               if (!str2hex(value_str, strlen(value_str), key, 16))
+                       return false;;
+       }
+
+       node = node_new();
+
+       if (!node)
+               return false;
+
+       node_set_device_key(node, key);
+
+       json_object_object_get_ex(jnode, "IVindex", &jint);
+       if (jint)
+               idx = json_object_get_int(jint);
+       else
+               idx = 0;
+
+       node_set_iv_index(node, idx);
+       if (local) {
+               bool update = false;
+               json_object_object_get_ex(jnode, "IVupdate", &jint);
+               if (jint)
+                       update = json_object_get_int(jint) ? true : false;
+               net_set_iv_index(idx, update);
+       }
+
+       if (json_object_object_get_ex(jnode, "sequenceNumber", &jint) &&
+                                                                       jint) {
+               int seq = json_object_get_int(jint);
+               node_set_sequence_number(node, seq);
+       }
+
+       /* Composition is mandatory for local node */
+       json_object_object_get_ex(jnode, "composition", &jconfig);
+       if ((jconfig && !parse_node_composition(node, jconfig)) ||
+                                                       (!jconfig && local)) {
+               node_free(node);
+               return false;
+       }
+
+       /* Configuration is mandatory for nodes in provisioning database */
+       json_object_object_get_ex(jnode, "configuration", &jconfig);
+       if (!jconfig) {
+               if (local) {
+                       /* This is an unprovisioned local device */
+                       goto done;
+               } else {
+                       node_free(node);
+                       return false;
+               }
+       }
+
+       json_object_object_get_ex(jconfig, "elements", &jelements);
+       if (!jelements) {
+               node_free(node);
+               return false;
+       }
+
+       if (!parse_configuration_elements(node, jelements, local)) {
+               node_free(node);
+               return false;;
+       }
+
+       json_object_object_get_ex(jconfig, "netKeys", &jidxs);
+       if (!jidxs || (parse_node_keys(node, jidxs, false) == 0)) {
+               node_free(node);
+               return false;
+       }
+
+       json_object_object_get_ex(jconfig, "appKeys", &jidxs);
+       if (jidxs)
+               parse_node_keys(node, jidxs, true);
+
+       json_object_object_get_ex(jconfig, "defaultTTL", &jvalue);
+       if (jvalue) {
+               int ttl = json_object_get_int(jvalue);
+               node_set_default_ttl(node, ttl &TTL_MASK);
+       }
+
+done:
+       if (local && !node_set_local_node(node)) {
+               node_free(node);
+               return false;
+       }
+
+       return true;
+}
+
+bool prov_db_show(const char *filename)
+{
+       char *str;
+
+       str = prov_file_read(filename);
+       if (!str)
+               return false;
+
+       bt_shell_printf("%s\n", str);
+       g_free(str);
+       return true;
+}
+
+static bool read_json_db(const char *filename, bool provisioner, bool local)
+{
+       char *str;
+       json_object *jmain;
+       json_object *jarray;
+       json_object *jprov;
+       json_object *jvalue;
+       json_object *jtemp;
+       uint8_t key[16];
+       int value_int;
+       char *value_str;
+       int len;
+       int i;
+       uint32_t index;
+       bool refresh = false;
+       bool res = false;
+
+       str = prov_file_read(filename);
+       if (!str) return false;
+
+       jmain = json_tokener_parse(str);
+       if (!jmain)
+               goto done;
+
+       if (local) {
+               json_object *jnode;
+               bool result;
+
+               json_object_object_get_ex(jmain, "node", &jnode);
+               if (!jnode) {
+                       bt_shell_printf("Cannot find \"node\" object");
+                       goto done;
+               } else
+                       result = parse_node(jnode, true);
+
+               /*
+               * If local node is provisioner, the rest of mesh settings
+               * are read from provisioning database.
+               */
+               if (provisioner) {
+                       res = result;
+                       goto done;
+               }
+       }
+
+       /* IV index */
+       json_object_object_get_ex(jmain, "IVindex", &jvalue);
+       if (!jvalue)
+               goto done;
+
+       index = json_object_get_int(jvalue);
+
+       json_object_object_get_ex(jmain, "IVupdate", &jvalue);
+       if (!jvalue)
+               goto done;
+
+       value_int = json_object_get_int(jvalue);
+
+       net_set_iv_index(index, value_int);
+
+       /* Network key(s) */
+       json_object_object_get_ex(jmain, "netKeys", &jarray);
+       if (!jarray)
+               goto done;
+
+       len = json_object_array_length(jarray);
+       bt_shell_printf("# netkeys = %d\n", len);
+
+       for (i = 0; i < len; ++i) {
+               uint32_t idx;
+
+               jtemp = json_object_array_get_idx(jarray, i);
+               json_object_object_get_ex(jtemp, "index", &jvalue);
+               if (!jvalue)
+                       goto done;
+               idx = json_object_get_int(jvalue);
+
+               json_object_object_get_ex(jtemp, "key", &jvalue);
+               if (!jvalue) {
+                       if (!mesh_get_random_bytes(key, 16))
+                               goto done;
+                       add_key(jtemp, "key", key);
+                       refresh = true;
+               } else {
+                       value_str = (char *)json_object_get_string(jvalue);
+                       if (!str2hex(value_str, strlen(value_str), key, 16)) {
+                               goto done;
+                       }
+               }
+
+               if (!keys_net_key_add(idx, key, false))
+                       goto done;
+
+               json_object_object_get_ex(jtemp, "keyRefresh", &jvalue);
+               if (!jvalue)
+                       goto done;
+
+               keys_set_kr_phase(idx, (uint8_t) json_object_get_int(jvalue));
+       }
+
+       /* App keys */
+       json_object_object_get_ex(jmain, "appKeys", &jarray);
+       if (jarray) {
+               len = json_object_array_length(jarray);
+               bt_shell_printf("# appkeys = %d\n", len);
+
+               for (i = 0; i < len; ++i) {
+                       int app_idx;
+                       int net_idx;
+
+                       jtemp = json_object_array_get_idx(jarray, i);
+                       json_object_object_get_ex(jtemp, "index",
+                                               &jvalue);
+                       if (!jvalue)
+                               goto done;
+
+                       app_idx = json_object_get_int(jvalue);
+                       if (!CHECK_KEY_IDX_RANGE(app_idx))
+                               goto done;
+
+                       json_object_object_get_ex(jtemp, "key", &jvalue);
+                       if (!jvalue) {
+                               if (!mesh_get_random_bytes(key, 16))
+                                       goto done;
+                               add_key(jtemp, "key", key);
+                               refresh = true;
+                       } else {
+                               value_str =
+                                       (char *)json_object_get_string(jvalue);
+                               str2hex(value_str, strlen(value_str), key, 16);
+                       }
+
+                       json_object_object_get_ex(jtemp, "boundNetKey",
+                                                       &jvalue);
+                       if (!jvalue)
+                               goto done;
+
+                       net_idx = json_object_get_int(jvalue);
+                       if (!CHECK_KEY_IDX_RANGE(net_idx))
+                               goto done;
+
+                       keys_app_key_add(net_idx, app_idx, key, false);
+               }
+       }
+
+       /* Provisioner info */
+       json_object_object_get_ex(jmain, "provisioners", &jarray);
+       if (!jarray)
+               goto done;
+
+       len = json_object_array_length(jarray);
+       bt_shell_printf("# provisioners = %d\n", len);
+
+       for (i = 0; i < len; ++i) {
+
+               jprov = json_object_array_get_idx(jarray, i);
+
+               /* Allocated unicast range */
+               json_object_object_get_ex(jprov, "allocatedUnicastRange",
+                                               &jtemp);
+               if (!jtemp) {
+                       goto done;
+               }
+
+               if (!parse_unicast_range(jtemp)) {
+                       bt_shell_printf("Doneed to parse unicast range\n");
+                       goto done;
+               }
+       }
+
+       json_object_object_get_ex(jmain, "nodes", &jarray);
+       if (!jarray) {
+               res = true;
+               goto done;
+       }
+
+       len = json_object_array_length(jarray);
+
+       bt_shell_printf("# provisioned nodes = %d\n", len);
+       for (i = 0; i < len; ++i) {
+               json_object *jnode;
+               jnode = json_object_array_get_idx(jarray, i);
+
+               if (!jnode || !parse_node(jnode, false))
+                       goto done;
+       }
+
+       res = true;
+done:
+
+       g_free(str);
+
+       if (res && refresh)
+               prov_file_write(jmain, false);
+
+       if (jmain)
+               json_object_put(jmain);
+
+       return res;
+}
+
+bool prov_db_read(const char *filename)
+{
+       prov_filename = filename;
+       return read_json_db(filename, true, false);
+}
+
+bool prov_db_read_local_node(const char *filename, bool provisioner)
+{
+       local_filename = filename;
+       return read_json_db(filename, provisioner, true);
+}
diff --git a/mesh/prov-db.h b/mesh/prov-db.h
new file mode 100644 (file)
index 0000000..b1e4c62
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+bool prov_db_show(const char *filename);
+bool prov_db_read(const char *filename);
+bool prov_db_read_local_node(const char *filename, bool provisioner);
+bool prov_db_add_new_node(struct mesh_node *node);
+bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,
+                                                               uint16_t len);
+bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc);
+bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,
+                       uint32_t model_id, uint16_t app_idx);
+bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl);
+bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq);
+bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov);
+bool prov_db_local_set_seq_num(uint32_t seq_num);
+bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,
+                                                       uint32_t model_id,
+                                               struct mesh_publication *pub);
+void prov_db_print_node_composition(struct mesh_node *node);
diff --git a/mesh/prov.c b/mesh/prov.c
new file mode 100644 (file)
index 0000000..d1b8555
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+#include <wordexp.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ecc.h"
+#include "src/shared/shell.h"
+
+#include "gdbus/gdbus.h"
+#include "mesh/node.h"
+#include "mesh/gatt.h"
+#include "mesh/crypto.h"
+#include "mesh/mesh-net.h"
+#include "mesh/util.h"
+#include "mesh/agent.h"
+#include "mesh/prov.h"
+#include "mesh/net.h"
+
+/* Provisioning Security Levels */
+#define MESH_PROV_SEC_HIGH     2
+#define MESH_PROV_SEC_MED      1
+#define MESH_PROV_SEC_LOW      0
+
+
+#define PROV_INVITE    0x00
+#define PROV_CAPS      0x01
+#define PROV_START     0x02
+#define PROV_PUB_KEY   0x03
+#define PROV_INP_CMPLT 0x04
+#define PROV_CONFIRM   0x05
+#define PROV_RANDOM    0x06
+#define PROV_DATA      0x07
+#define PROV_COMPLETE  0x08
+#define PROV_FAILED    0x09
+
+#define PROV_NO_OOB    0
+#define PROV_STATIC_OOB        1
+#define PROV_OUTPUT_OOB        2
+#define PROV_INPUT_OOB 3
+
+#define PROV_ERR_INVALID_PDU           0x01
+#define PROV_ERR_INVALID_FORMAT                0x02
+#define PROV_ERR_UNEXPECTED_PDU                0x03
+#define PROV_ERR_CONFIRM_FAILED                0x04
+#define PROV_ERR_INSUF_RESOURCE                0x05
+#define PROV_ERR_DECRYPT_FAILED                0x06
+#define PROV_ERR_UNEXPECTED_ERR                0x07
+#define PROV_ERR_CANT_ASSIGN_ADDR      0x08
+
+/* For Deployment, Security levels below HIGH are *not* recomended */
+static uint8_t prov_sec_level = MESH_PROV_SEC_MED;
+
+/* Expected Provisioning PDU sizes */
+static const uint16_t expected_pdu_size[] = {
+       1 + 1,                                  /* PROV_INVITE */
+       1 + 1 + 2 + 1 + 1 + 1 + 2 + 1 + 2,      /* PROV_CAPS */
+       1 + 1 + 1 + 1 + 1 + 1,                  /* PROV_START */
+       1 + 64,                                 /* PROV_PUB_KEY */
+       1,                                      /* PROV_INP_CMPLT */
+       1 + 16,                                 /* PROV_CONFIRM */
+       1 + 16,                                 /* PROV_RANDOM */
+       1 + 16 + 2 + 1 + 4 + 2 + 8,             /* PROV_DATA */
+       1,                                      /* PROV_COMPLETE */
+       1 + 1,                                  /* PROV_FAILED */
+};
+
+typedef struct __packed {
+       uint8_t attention;
+} __attribute__ ((packed))     prov_invite;
+
+typedef struct {
+       uint8_t  num_ele;
+       uint16_t algorithms;
+       uint8_t  pub_type;
+       uint8_t  static_type;
+       uint8_t  output_size;
+       uint16_t output_action;
+       uint8_t  input_size;
+       uint16_t input_action;
+} __attribute__ ((packed))     prov_caps;
+
+typedef struct {
+       uint8_t algorithm;
+       uint8_t pub_key;
+       uint8_t auth_method;
+       uint8_t auth_action;
+       uint8_t auth_size;
+} __attribute__ ((packed))     prov_start;
+
+typedef struct {
+       prov_invite     invite;
+       prov_caps       caps;
+       prov_start      start;
+       uint8_t prv_pub_key[64];
+       uint8_t dev_pub_key[64];
+} __attribute__ ((packed))     conf_input;
+
+struct prov_data {
+       GDBusProxy              *prov_in;
+       provision_done_cb       prov_done;
+       void                    *user_data;
+       uint16_t                net_idx;
+       uint16_t                new_addr;
+       uint8_t                 state;
+       uint8_t                 eph_priv_key[32];
+       uint8_t                 ecdh_secret[32];
+       conf_input              conf_in;
+       uint8_t                 rand_auth[32];
+       uint8_t                 salt[16];
+       uint8_t                 conf_key[16];
+       uint8_t                 mesh_conf[16];
+       uint8_t                 dev_key[16];
+};
+
+static uint8_t u16_highest_bit(uint16_t mask)
+{
+       uint8_t cnt = 0;
+
+       if (!mask) return 0xff;
+
+       while (mask & 0xfffe) {
+               cnt++;
+               mask >>= 1;
+       }
+
+       return cnt;
+}
+
+bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,
+               provision_done_cb cb, void *user_data)
+{
+       uint8_t invite[] = { PROXY_PROVISIONING_PDU, PROV_INVITE, 0x10 };
+       struct prov_data *prov = node_get_prov(node);
+
+       if (prov) return false;
+
+       prov = g_new0(struct prov_data, 1);
+       prov->prov_in = prov_in;
+       prov->net_idx = net_idx;
+       prov->prov_done = cb;
+       prov->user_data = user_data;
+       node_set_prov(node, prov);
+       prov->conf_in.invite.attention = invite[2];
+       prov->state = PROV_INVITE;
+
+       bt_shell_printf("Open-Node: %p\n", node);
+       bt_shell_printf("Open-Prov: %p\n", prov);
+       bt_shell_printf("Open-Prov: proxy %p\n", prov_in);
+
+       return mesh_gatt_write(prov_in, invite, sizeof(invite), NULL, node);
+}
+
+static bool prov_send_prov_data(void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+       uint8_t out[35] = { PROXY_PROVISIONING_PDU, PROV_DATA };
+       uint8_t key[16];
+       uint8_t nonce[13];
+       uint64_t mic;
+
+       if (prov == NULL) return false;
+
+       mesh_crypto_session_key(prov->ecdh_secret, prov->salt, key);
+       mesh_crypto_nonce(prov->ecdh_secret, prov->salt, nonce);
+       mesh_crypto_device_key(prov->ecdh_secret, prov->salt, prov->dev_key);
+
+       print_byte_array("S-Key\t", key, sizeof(key));
+       print_byte_array("S-Nonce\t", nonce, sizeof(nonce));
+       print_byte_array("DevKey\t", prov->dev_key, sizeof(prov->dev_key));
+
+       if (!net_get_key(prov->net_idx, out + 2))
+               return false;
+
+       put_be16(prov->net_idx, out + 2 + 16);
+       net_get_flags(prov->net_idx, out + 2 + 16 + 2);
+       put_be32(net_get_iv_index(NULL), out + 2 + 16 + 2 + 1);
+       put_be16(prov->new_addr, out + 2 + 16 + 2 + 1 + 4);
+
+       print_byte_array("Data\t", out + 2, 16 + 2 + 1 + 4 + 2);
+
+       mesh_crypto_aes_ccm_encrypt(nonce, key,
+                                       NULL, 0,
+                                       out + 2,
+                                       sizeof(out) - 2 - sizeof(mic),
+                                       out + 2,
+                                       &mic, sizeof(mic));
+
+       print_byte_array("DataEncrypted + mic\t", out + 2, sizeof(out) - 2);
+
+       prov->state = PROV_DATA;
+       return mesh_gatt_write(prov->prov_in, out, sizeof(out), NULL, node);
+}
+
+static bool prov_send_confirm(void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+       uint8_t out[18] = { PROXY_PROVISIONING_PDU, PROV_CONFIRM };
+
+       if (prov == NULL) return false;
+
+       mesh_get_random_bytes(prov->rand_auth, 16);
+
+       mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+                               sizeof(prov->rand_auth), out + 2);
+
+       prov->state = PROV_CONFIRM;
+       return mesh_gatt_write(prov->prov_in, out, sizeof(out), NULL, node);
+}
+
+static void prov_out_oob_done(oob_type_t type, void *buf, uint16_t len,
+               void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+
+       if (prov == NULL) return;
+
+       switch (type) {
+               default:
+               case NONE:
+               case OUTPUT:
+                       prov_complete(node, PROV_ERR_INVALID_PDU);
+                       return;
+
+               case ASCII:
+               case HEXADECIMAL:
+                       if (len > 16)
+                               prov_complete(node, PROV_ERR_INVALID_PDU);
+
+                       memcpy(prov->rand_auth + 16, buf, len);
+                       break;
+
+               case DECIMAL:
+                       if (len != 4)
+                               prov_complete(node, PROV_ERR_INVALID_PDU);
+
+                       memcpy(prov->rand_auth +
+                                       sizeof(prov->rand_auth) -
+                                       sizeof(uint32_t),
+                                       buf, len);
+                       break;
+       }
+
+       prov_send_confirm(node);
+}
+
+static uint32_t power_ten(uint8_t power)
+{
+       uint32_t ret = 1;
+
+       while (power--)
+               ret *= 10;
+
+       return ret;
+}
+
+char *in_action[3] = {
+       "Push",
+       "Twist",
+       "Enter"
+};
+
+static void prov_calc_ecdh(DBusMessage *message, void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+       uint8_t action = prov->conf_in.start.auth_action;
+       uint8_t size = prov->conf_in.start.auth_size;
+       char in_oob_display[100];
+       uint8_t *tmp = (void *) in_oob_display;
+       uint32_t in_oob;
+
+       if (prov == NULL) return;
+
+       /* Convert to Mesh byte order */
+       memcpy(tmp, prov->conf_in.dev_pub_key, 64);
+       swap_u256_bytes(tmp);
+       swap_u256_bytes(tmp + 32);
+
+       ecdh_shared_secret(tmp, prov->eph_priv_key, prov->ecdh_secret);
+
+       /* Convert to Mesh byte order */
+       swap_u256_bytes(prov->ecdh_secret);
+
+       mesh_crypto_s1(&prov->conf_in,
+                       sizeof(prov->conf_in), prov->salt);
+
+       mesh_crypto_prov_conf_key(prov->ecdh_secret,
+                       prov->salt, prov->conf_key);
+
+       switch (prov->conf_in.start.auth_method) {
+               default:
+                       prov_complete(node, PROV_ERR_INVALID_PDU);
+                       break;
+
+               case 0: /* No OOB */
+                       prov_send_confirm(node);
+                       break;
+
+               case 1: /* Static OOB */
+                       agent_input_request(HEXADECIMAL,
+                                       16,
+                                       prov_out_oob_done, node);
+                       break;
+
+               case 2: /* Output OOB */
+                       if (action <= 3)
+                               agent_input_request(DECIMAL,
+                                               size,
+                                               prov_out_oob_done, node);
+                       else
+                               agent_input_request(ASCII,
+                                               size,
+                                               prov_out_oob_done, node);
+                       break;
+
+               case 3: /* Input OOB */
+
+                       if (action <= 2) {
+                               mesh_get_random_bytes(&in_oob, sizeof(in_oob));
+                               in_oob %= power_ten(size);
+                               sprintf(in_oob_display, "%s %d on device\n",
+                                       in_action[action], in_oob);
+                               put_be32(in_oob,
+                                               prov->rand_auth +
+                                               sizeof(prov->rand_auth) -
+                                               sizeof(uint32_t));
+                       } else {
+                               uint8_t in_ascii[9];
+                               int i = size;
+
+                               mesh_get_random_bytes(in_ascii, i);
+
+                               while (i--) {
+                                       in_ascii[i] =
+                                               in_ascii[i] % ((26 * 2) + 10);
+                                       if (in_ascii[i] >= 10 + 26)
+                                               in_ascii[i] += 'a' - (10 + 26);
+                                       else if (in_ascii[i] >= 10)
+                                               in_ascii[i] += 'A' - 10;
+                                       else
+                                               in_ascii[i] += '0';
+                               }
+                               in_ascii[size] = '\0';
+                               memcpy(prov->rand_auth + 16, in_ascii, size);
+                               sprintf(in_oob_display,
+                                               "Enter %s on device\n",
+                                               in_ascii);
+                       }
+                       bt_shell_printf("Agent String: %s\n", in_oob_display);
+                       agent_output_request(in_oob_display);
+                       break;
+       }
+}
+
+static void prov_send_pub_key(struct mesh_node *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+       uint8_t out[66] = { PROXY_PROVISIONING_PDU, PROV_PUB_KEY };
+       GDBusReturnFunction cb = NULL;
+
+       if (prov == NULL) return;
+
+       if (prov->conf_in.start.pub_key)
+               cb = prov_calc_ecdh;
+
+       memcpy(out + 2, prov->conf_in.prv_pub_key, 64);
+       prov->state = PROV_PUB_KEY;
+       mesh_gatt_write(prov->prov_in, out, 66, cb, node);
+}
+
+static void prov_oob_pub_key(oob_type_t type, void *buf, uint16_t len,
+               void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+
+       if (prov == NULL) return;
+
+       memcpy(prov->conf_in.dev_pub_key, buf, 64);
+       prov_send_pub_key(node);
+}
+
+static void prov_start_cmplt(DBusMessage *message, void *node)
+{
+       struct prov_data *prov = node_get_prov(node);
+
+       if (prov == NULL) return;
+
+       if (prov->conf_in.start.pub_key)
+               agent_input_request(HEXADECIMAL, 64, prov_oob_pub_key, node);
+       else
+               prov_send_pub_key(node);
+}
+
+bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len)
+{
+       struct prov_data *prov = node_get_prov(node);
+       uint8_t sec_level = MESH_PROV_SEC_HIGH;
+       uint8_t out[35] = { PROXY_PROVISIONING_PDU };
+
+       if (prov == NULL || len < 2) return false;
+
+       buf++;
+       len--;
+
+       bt_shell_printf("Got provisioning data (%d bytes)\n", len);
+
+       if (buf[0] > PROV_FAILED || expected_pdu_size[buf[0]] != len)
+               return prov_complete(node, PROV_ERR_INVALID_PDU);
+
+       print_byte_array("\t", buf, len);
+
+       if (buf[0] == PROV_FAILED)
+               return prov_complete(node, buf[1]);
+
+       /* Check provisioning state */
+       switch (prov->state) {
+               default:
+                       return prov_complete(node, PROV_ERR_INVALID_PDU);
+
+               case PROV_INVITE:
+
+                       if (buf[0] != PROV_CAPS)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       /* Normalize to beginning of packed Param struct */
+                       buf++;
+                       len--;
+
+                       /* Save Capability values */
+                       memcpy(&prov->conf_in.caps, buf, len);
+
+                       sec_level = prov_get_sec_level();
+
+                       if (sec_level == MESH_PROV_SEC_HIGH) {
+
+                               /* Enforce High Security */
+                               if (prov->conf_in.caps.pub_type != 1 &&
+                                       prov->conf_in.caps.static_type != 1)
+                                       return prov_complete(node,
+                                                       PROV_ERR_INVALID_PDU);
+
+                       } else if (sec_level == MESH_PROV_SEC_MED) {
+
+                               /* Enforce Medium Security */
+                               if (prov->conf_in.caps.pub_type != 1 &&
+                                       prov->conf_in.caps.static_type != 1 &&
+                                       prov->conf_in.caps.input_size == 0 &&
+                                       prov->conf_in.caps.output_size == 0)
+                                       return prov_complete(node,
+                                                       PROV_ERR_INVALID_PDU);
+
+                       }
+
+                       /* Num Elements cannot be Zero */
+                       if (prov->conf_in.caps.num_ele == 0)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       /* All nodes must support Algorithm 0x0001 */
+                       if (!(get_be16(buf + 1) & 0x0001))
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       /* Pub Key and Static type may not be > 1 */
+                       if (prov->conf_in.caps.pub_type > 0x01 ||
+                                       prov->conf_in.caps.static_type > 0x01)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       prov->new_addr =
+                               net_obtain_address(prov->conf_in.caps.num_ele);
+
+                       if (!prov->new_addr)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       out[1] = PROV_START;
+                       prov->conf_in.start.algorithm = 0;
+                       prov->conf_in.start.pub_key =
+                               prov->conf_in.caps.pub_type;
+
+                       /* Compose START based on most secure values */
+                       if (prov->conf_in.caps.static_type) {
+
+                               prov->conf_in.start.auth_method =
+                                       PROV_STATIC_OOB;
+
+                       } else if (prov->conf_in.caps.output_size >
+                                       prov->conf_in.caps.input_size) {
+
+                               prov->conf_in.start.auth_method =
+                                       PROV_OUTPUT_OOB;
+                               prov->conf_in.start.auth_action =
+                                       u16_highest_bit(get_be16(buf + 6));
+                               prov->conf_in.start.auth_size =
+                                       prov->conf_in.caps.output_size;
+
+                       } else if (prov->conf_in.caps.input_size > 0) {
+
+                               prov->conf_in.start.auth_method =
+                                       PROV_INPUT_OOB;
+                               prov->conf_in.start.auth_action =
+                                       u16_highest_bit(get_be16(buf + 9));
+                               prov->conf_in.start.auth_size =
+                                       prov->conf_in.caps.input_size;
+                       }
+
+                       /* Range Check START values */
+                       if (prov->conf_in.start.auth_size > 8)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       prov->state = PROV_START;
+
+                       memcpy(out + 2, &prov->conf_in.start, 5);
+
+                       ecc_make_key(prov->conf_in.prv_pub_key,
+                                       prov->eph_priv_key);
+
+                       /* Swap public key to share into Mesh byte ordering */
+                       swap_u256_bytes(prov->conf_in.prv_pub_key);
+                       swap_u256_bytes(prov->conf_in.prv_pub_key + 32);
+
+                       return mesh_gatt_write(prov->prov_in, out, 7,
+                                       prov_start_cmplt, node);
+
+
+               case PROV_PUB_KEY:
+                       if (buf[0] == PROV_PUB_KEY &&
+                                       !prov->conf_in.start.pub_key) {
+
+                               memcpy(prov->conf_in.dev_pub_key, buf + 1, 64);
+                               prov_calc_ecdh(NULL, node);
+                               return true;
+
+                       } else if (buf[0] == PROV_INP_CMPLT) {
+                               agent_output_request_cancel();
+                               return prov_send_confirm(node);
+                       } else
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+               case PROV_CONFIRM:
+                       if (buf[0] != PROV_CONFIRM)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       memcpy(prov->mesh_conf, buf + 1, 16);
+
+                       out[1] = PROV_RANDOM;
+                       memcpy(out + 2, prov->rand_auth, 16);
+
+                       prov->state = PROV_RANDOM;
+                       return mesh_gatt_write(prov->prov_in, out, 18,
+                                       NULL, node);
+
+               case PROV_RANDOM:
+                       if (buf[0] != PROV_RANDOM)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       /* Calculate New Salt while we still have
+                        * both random values */
+                       mesh_crypto_prov_prov_salt(prov->salt,
+                                                       prov->rand_auth,
+                                                       buf + 1,
+                                                       prov->salt);
+
+                       /* Calculate meshs Conf Value */
+                       memcpy(prov->rand_auth, buf + 1, 16);
+                       mesh_crypto_aes_cmac(prov->conf_key, prov->rand_auth,
+                               sizeof(prov->rand_auth), out + 1);
+
+                       /* Validate Mesh confirmation */
+                       if (memcmp(out + 1, prov->mesh_conf, 16) != 0)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       bt_shell_printf("Confirmation Validated\n");
+
+                       prov_send_prov_data(node);
+
+                       return true;
+
+               case PROV_DATA:
+                       if (buf[0] != PROV_COMPLETE)
+                               return prov_complete(node,
+                                               PROV_ERR_INVALID_PDU);
+
+                       return prov_complete(node, 0);
+       }
+
+
+
+       /* Compose appropriate reply for the prov state message */
+       /* Send reply via mesh_gatt_write() */
+       /* If done, call prov_done calllback and free prov housekeeping data */
+       bt_shell_printf("Got provisioning data (%d bytes)\n", len);
+       print_byte_array("\t", buf, len);
+
+       return true;
+}
+
+bool prov_complete(struct mesh_node *node, uint8_t status)
+{
+       struct prov_data *prov = node_get_prov(node);
+       void *user_data;
+       provision_done_cb cb;
+
+       if (prov == NULL) return false;
+
+       if (status && prov->new_addr && prov->conf_in.caps.num_ele) {
+               net_release_address(prov->new_addr, prov->conf_in.caps.num_ele);
+       }
+
+       if (!status) {
+               node_set_num_elements(node, prov->conf_in.caps.num_ele);
+               node_set_primary(node, prov->new_addr);
+               node_set_device_key(node, prov->dev_key);
+               node_net_key_add(node, prov->net_idx);
+       }
+
+       user_data = prov->user_data;
+       cb = prov->prov_done;
+       g_free(prov);
+       node_set_prov(node, NULL);
+       if (cb) cb(user_data, status);
+
+       return true;
+}
+
+bool prov_set_sec_level(uint8_t level)
+{
+       if (level > MESH_PROV_SEC_HIGH)
+               return false;
+
+       prov_sec_level = level;
+
+       return true;
+}
+
+uint8_t prov_get_sec_level(void)
+{
+       return prov_sec_level;
+}
old mode 100755 (executable)
new mode 100644 (file)
similarity index 53%
rename from android/bas.h
rename to mesh/prov.h
index 3e175b5..2587df8
@@ -2,15 +2,15 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2017  Intel Corporation. All rights reserved.
  *
  *
- *  This library is free software; you can rebastribute it and/or
+ *  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 bastributed 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
  *  Lesser General Public License for more details.
  *
  */
 
-struct bt_bas;
+struct prov;
 
-struct bt_bas *bt_bas_new(void *primary);
+typedef void (*provision_done_cb)(void *user_data, int status);
 
-struct bt_bas *bt_bas_ref(struct bt_bas *bas);
-void bt_bas_unref(struct bt_bas *bas);
-
-bool bt_bas_attach(struct bt_bas *bas, void *gatt);
-void bt_bas_detach(struct bt_bas *bas);
+bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,
+               provision_done_cb cb, void *user_data);
+bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len);
+bool prov_complete(struct mesh_node *node, uint8_t status);
+bool prov_set_sec_level(uint8_t level);
+uint8_t prov_get_sec_level(void);
diff --git a/mesh/prov_db.json b/mesh/prov_db.json
new file mode 100644 (file)
index 0000000..74a0312
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "$schema":"file:\/\/\/BlueZ\/Mesh\/schema\/mesh.jsonschema",
+  "meshName":"BT Mesh",
+  "IVindex":5,
+  "IVupdate":0,
+  "netKeys":[
+    {
+      "index":0,
+      "keyRefresh":0,
+      "key":"18eed9c2a56add85049ffc3c59ad0e12"
+    }
+  ],
+  "appKeys":[
+    {
+      "index":0,
+      "boundNetKey":0,
+      "key":"4f68ad85d9f48ac8589df665b6b49b8a"
+    },
+    {
+      "index":1,
+      "boundNetKey":0,
+      "key":"2aa2a6ded5a0798ceab5787ca3ae39fc"
+    }
+  ],
+  "provisioners":[
+    {
+      "provisionerName":"BT Mesh Provisioner",
+      "unicastAddress":"0077",
+      "allocatedUnicastRange":[
+        {
+          "lowAddress":"0100",
+          "highAddress":"7fff"
+        }
+      ]
+    }
+  ],
+}
diff --git a/mesh/tags b/mesh/tags
new file mode 100644 (file)
index 0000000..2309514
--- /dev/null
+++ b/mesh/tags
@@ -0,0 +1,991 @@
+!_TAG_FILE_FORMAT      2       /extended format; --format=1 will not append ;" to lines/
+!_TAG_FILE_SORTED      1       /0=unsorted, 1=sorted, 2=foldcase/
+!_TAG_PROGRAM_AUTHOR   Darren Hiebert  /dhiebert@users.sourceforge.net/
+!_TAG_PROGRAM_NAME     Exuberant Ctags //
+!_TAG_PROGRAM_URL      http://ctags.sourceforge.net    /official site/
+!_TAG_PROGRAM_VERSION  5.9~svn20110310 //
+ACTION_ADD     node.h  26;"    d
+ACTION_DELETE  node.h  28;"    d
+ACTION_UPDATE  node.h  27;"    d
+AKF_BIT        net.c   144;"   d       file:
+AKF_HDR_SHIFT  mesh-net.h      87;"    d
+ALG_SET_AEAD_AUTHSIZE  crypto.c        42;"    d       file:
+ALL_NODES_ADDRESS      mesh-net.h      51;"    d
+APP_IDX_ANY    mesh-net.h      37;"    d
+APP_IDX_DEV    mesh-net.h      36;"    d
+APP_IDX_INVALID        mesh-net.h      39;"    d
+APP_IDX_MASK   mesh-net.h      35;"    d
+APP_IDX_NET    mesh-net.h      38;"    d
+ASCII  agent.h /^      ASCII,$/;"      e       enum:__anon2
+BLACKLIST_FILTER       net.c   217;"   d       file:
+CHECK_KEY_IDX_RANGE    prov-db.c       53;"    d       file:
+CMAC_MSG_MAX   crypto.c        161;"   d       file:
+COLORED_CHG    main.c  64;"    d       file:
+COLORED_DEL    main.c  65;"    d       file:
+COLORED_NEW    main.c  63;"    d       file:
+CONFIG_CLIENT_MODEL_ID config-model.h  25;"    d
+CONFIG_SERVER_MODEL_ID config-model.h  24;"    d
+CONN_TYPE_IDENTITY     main.c  103;"   d       file:
+CONN_TYPE_INVALID      main.c  105;"   d       file:
+CONN_TYPE_NETWORK      main.c  102;"   d       file:
+CONN_TYPE_PROVISION    main.c  104;"   d       file:
+CREDFLAG_MASK  mesh-net.h      34;"    d
+CTL    mesh-net.h      30;"    d
+DECIMAL        agent.h /^      DECIMAL,$/;"    e       enum:__anon2
+DECIMAL_OOB_LEN        agent.h 25;"    d
+DEFAULT_TTL    mesh-net.h      57;"    d
+DISTANCE_VAL_INVALID   main.c  1487;"  d       file:
+FILTER_ADD     net.c   211;"   d       file:
+FILTER_DEL     net.c   212;"   d       file:
+FILTER_SETUP   net.c   210;"   d       file:
+FILTER_STATUS  net.c   213;"   d       file:
+FRIENDS_ADDRESS        mesh-net.h      49;"    d
+FRND_CACHE_MAX mesh-net.h      45;"    d
+GATT_MTU       gatt.c  95;"    d       file:
+GATT_SAR_COMPLETE      gatt.h  30;"    d
+GATT_SAR_CONTINUE      gatt.h  32;"    d
+GATT_SAR_FIRST gatt.h  31;"    d
+GATT_SAR_LAST  gatt.h  33;"    d
+GATT_SAR_MASK  gatt.h  29;"    d
+GATT_TYPE_INVALID      gatt.h  34;"    d
+GATT_TYPE_MASK gatt.h  35;"    d
+GENERIC_ONOFF_CLIENT_MODEL_ID  onoff-model.h   25;"    d
+GENERIC_ONOFF_SERVER_MODEL_ID  onoff-model.h   24;"    d
+GROUP_ADDRESS_HIGH     mesh-net.h      55;"    d
+GROUP_ADDRESS_LOW      mesh-net.h      54;"    d
+HAS_APP_KEY    mesh-net.h      89;"    d
+HAS_MIC64      mesh-net.h      100;"   d
+HEXADECIMAL    agent.h /^      HEXADECIMAL,$/;"        e       enum:__anon2
+IS_ALL_NODES   mesh-net.h      76;"    d
+IS_GROUP       mesh-net.h      74;"    d
+IS_RELAYED     mesh-net.h      99;"    d
+IS_SEGMENTED   mesh-net.h      81;"    d
+IS_UNASSIGNED  mesh-net.h      69;"    d
+IS_UNICAST     mesh-net.h      70;"    d
+IS_VIRTUAL     mesh-net.h      72;"    d
+IV_IDX_DIFF_RANGE      net.c   225;"   d       file:
+IV_UPD_INIT    net.c   220;"   d       file:
+IV_UPD_NORMAL  net.c   221;"   d       file:
+IV_UPD_NORMAL_HOLD     net.c   223;"   d       file:
+IV_UPD_UPDATING        net.c   222;"   d       file:
+KEY_AID_MASK   mesh-net.h      84;"    d
+KEY_AID_SHIFT  mesh-net.h      86;"    d
+KEY_CACHE_SIZE mesh-net.h      44;"    d
+KEY_HDR_SHIFT  mesh-net.h      88;"    d
+KEY_ID_AKF     mesh-net.h      85;"    d
+KEY_ID_MASK    mesh-net.h      83;"    d
+KR_PHASE_INVALID       keys.h  27;"    d
+KR_PHASE_NONE  keys.h  24;"    d
+KR_PHASE_ONE   keys.h  25;"    d
+KR_PHASE_TWO   keys.h  26;"    d
+MAX_ASCII_OOB_LEN      agent.h 26;"    d
+MAX_GATT_SIZE  gatt.h  27;"    d
+MAX_HEXADECIMAL_OOB_LEN        agent.h 24;"    d
+MAX_SEG_LEN    mesh-net.h      62;"    d
+MAX_SEG_TO_LEN mesh-net.h      66;"    d
+MAX_UNSEG_LEN  mesh-net.h      61;"    d
+MESH_FEATURE_FRIEND    mesh-net.h      138;"   d
+MESH_FEATURE_LPN       mesh-net.h      139;"   d
+MESH_FEATURE_PROXY     mesh-net.h      137;"   d
+MESH_FEATURE_RELAY     mesh-net.h      136;"   d
+MESH_MAX_ACCESS_PAYLOAD        mesh-net.h      141;"   d
+MESH_PROV_DATA_IN_UUID_STR     main.c  70;"    d       file:
+MESH_PROV_DATA_OUT_UUID_STR    gatt.c  49;"    d       file:
+MESH_PROV_DATA_OUT_UUID_STR    main.c  71;"    d       file:
+MESH_PROV_SEC_HIGH     prov.c  54;"    d       file:
+MESH_PROV_SEC_LOW      prov.c  56;"    d       file:
+MESH_PROV_SEC_MED      prov.c  55;"    d       file:
+MESH_PROXY_DATA_IN_UUID_STR    main.c  72;"    d       file:
+MESH_PROXY_DATA_OUT_UUID_STR   gatt.c  50;"    d       file:
+MESH_PROXY_DATA_OUT_UUID_STR   main.c  73;"    d       file:
+MESH_STATUS_CANNOT_BIND        mesh-net.h      156;"   d
+MESH_STATUS_CANNOT_REMOVE      mesh-net.h      155;"   d
+MESH_STATUS_CANNOT_SET mesh-net.h      158;"   d
+MESH_STATUS_CANNOT_UPDATE      mesh-net.h      154;"   d
+MESH_STATUS_FEAT_NOT_SUP       mesh-net.h      153;"   d
+MESH_STATUS_IDX_ALREADY_STORED mesh-net.h      149;"   d
+MESH_STATUS_INSUFF_RESOURCES   mesh-net.h      148;"   d
+MESH_STATUS_INVALID_ADDRESS    mesh-net.h      144;"   d
+MESH_STATUS_INVALID_APPKEY     mesh-net.h      146;"   d
+MESH_STATUS_INVALID_BINDING    mesh-net.h      160;"   d
+MESH_STATUS_INVALID_MODEL      mesh-net.h      145;"   d
+MESH_STATUS_INVALID_NETKEY     mesh-net.h      147;"   d
+MESH_STATUS_INVALID_PUB_PARAM  mesh-net.h      150;"   d
+MESH_STATUS_NOT_SUB_MOD        mesh-net.h      151;"   d
+MESH_STATUS_STORAGE_FAIL       mesh-net.h      152;"   d
+MESH_STATUS_SUCCESS    mesh-net.h      143;"   d
+MESH_STATUS_UNABLE_CHANGE_STATE        mesh-net.h      157;"   d
+MESH_STATUS_UNSPECIFIED_ERROR  mesh-net.h      159;"   d
+MIN_COMPOSITION_LEN    config-client.c 50;"    d       file:
+NET_IDX_INVALID        main.c  107;"   d       file:
+NET_IDX_INVALID        mesh-net.h      41;"    d
+NET_IDX_PRIMARY        mesh-net.h      42;"    d
+NET_KEY_REFRESH_PHASE_NONE     mesh-net.h      131;"   d
+NET_KEY_REFRESH_PHASE_ONE      mesh-net.h      132;"   d
+NET_KEY_REFRESH_PHASE_THREE    mesh-net.h      134;"   d
+NET_KEY_REFRESH_PHASE_TWO      mesh-net.h      133;"   d
+NET_OP_FRND_CLEAR      mesh-net.h      122;"   d
+NET_OP_FRND_CLEAR_CONFIRM      mesh-net.h      123;"   d
+NET_OP_FRND_OFFER      mesh-net.h      121;"   d
+NET_OP_FRND_POLL       mesh-net.h      118;"   d
+NET_OP_FRND_REQUEST    mesh-net.h      120;"   d
+NET_OP_FRND_UPDATE     mesh-net.h      119;"   d
+NET_OP_HEARTBEAT       mesh-net.h      128;"   d
+NET_OP_PROXY_SUB_ADD   mesh-net.h      125;"   d
+NET_OP_PROXY_SUB_CONFIRM       mesh-net.h      127;"   d
+NET_OP_PROXY_SUB_REMOVE        mesh-net.h      126;"   d
+NET_OP_SEG_ACKNOWLEDGE mesh-net.h      117;"   d
+NONE   agent.h /^      NONE,$/;"       e       enum:__anon2
+OPCODE_HDR_SHIFT       mesh-net.h      92;"    d
+OPCODE_MASK    mesh-net.h      91;"    d
+OP_APPKEY_ADD  config-model.h  27;"    d
+OP_APPKEY_DELETE       config-model.h  28;"    d
+OP_APPKEY_GET  config-model.h  29;"    d
+OP_APPKEY_LIST config-model.h  30;"    d
+OP_APPKEY_STATUS       config-model.h  31;"    d
+OP_APPKEY_UPDATE       config-model.h  32;"    d
+OP_CONFIG_BEACON_GET   config-model.h  35;"    d
+OP_CONFIG_BEACON_SET   config-model.h  36;"    d
+OP_CONFIG_BEACON_STATUS        config-model.h  37;"    d
+OP_CONFIG_DEFAULT_TTL_GET      config-model.h  38;"    d
+OP_CONFIG_DEFAULT_TTL_SET      config-model.h  39;"    d
+OP_CONFIG_DEFAULT_TTL_STATUS   config-model.h  40;"    d
+OP_CONFIG_FRIEND_GET   config-model.h  41;"    d
+OP_CONFIG_FRIEND_SET   config-model.h  42;"    d
+OP_CONFIG_FRIEND_STATUS        config-model.h  43;"    d
+OP_CONFIG_HEARTBEAT_PUB_GET    config-model.h  74;"    d
+OP_CONFIG_HEARTBEAT_PUB_SET    config-model.h  75;"    d
+OP_CONFIG_HEARTBEAT_PUB_STATUS config-model.h  76;"    d
+OP_CONFIG_HEARTBEAT_SUB_GET    config-model.h  77;"    d
+OP_CONFIG_HEARTBEAT_SUB_SET    config-model.h  78;"    d
+OP_CONFIG_HEARTBEAT_SUB_STATUS config-model.h  79;"    d
+OP_CONFIG_KEY_REFRESH_PHASE_GET        config-model.h  47;"    d
+OP_CONFIG_KEY_REFRESH_PHASE_SET        config-model.h  48;"    d
+OP_CONFIG_KEY_REFRESH_PHASE_STATUS     config-model.h  49;"    d
+OP_CONFIG_MODEL_PUB_GET        config-model.h  50;"    d
+OP_CONFIG_MODEL_PUB_SET        config-model.h  51;"    d
+OP_CONFIG_MODEL_PUB_STATUS     config-model.h  52;"    d
+OP_CONFIG_MODEL_PUB_VIRT_SET   config-model.h  53;"    d
+OP_CONFIG_MODEL_SUB_ADD        config-model.h  54;"    d
+OP_CONFIG_MODEL_SUB_DELETE     config-model.h  55;"    d
+OP_CONFIG_MODEL_SUB_DELETE_ALL config-model.h  56;"    d
+OP_CONFIG_MODEL_SUB_GET        config-model.h  68;"    d
+OP_CONFIG_MODEL_SUB_LIST       config-model.h  69;"    d
+OP_CONFIG_MODEL_SUB_OVERWRITE  config-model.h  57;"    d
+OP_CONFIG_MODEL_SUB_STATUS     config-model.h  58;"    d
+OP_CONFIG_MODEL_SUB_VIRT_ADD   config-model.h  59;"    d
+OP_CONFIG_MODEL_SUB_VIRT_DELETE        config-model.h  60;"    d
+OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE     config-model.h  61;"    d
+OP_CONFIG_NETWORK_TRANSMIT_GET config-model.h  62;"    d
+OP_CONFIG_NETWORK_TRANSMIT_SET config-model.h  63;"    d
+OP_CONFIG_NETWORK_TRANSMIT_STATUS      config-model.h  64;"    d
+OP_CONFIG_POLL_TIMEOUT_LIST    config-model.h  72;"    d
+OP_CONFIG_POLL_TIMEOUT_STATUS  config-model.h  73;"    d
+OP_CONFIG_PROXY_GET    config-model.h  44;"    d
+OP_CONFIG_PROXY_SET    config-model.h  45;"    d
+OP_CONFIG_PROXY_STATUS config-model.h  46;"    d
+OP_CONFIG_RELAY_GET    config-model.h  65;"    d
+OP_CONFIG_RELAY_SET    config-model.h  66;"    d
+OP_CONFIG_RELAY_STATUS config-model.h  67;"    d
+OP_CONFIG_VEND_MODEL_SUB_GET   config-model.h  70;"    d
+OP_CONFIG_VEND_MODEL_SUB_LIST  config-model.h  71;"    d
+OP_DEV_COMP_GET        config-model.h  33;"    d
+OP_DEV_COMP_STATUS     config-model.h  34;"    d
+OP_GENERIC_ONOFF_GET   onoff-model.h   27;"    d
+OP_GENERIC_ONOFF_SET   onoff-model.h   28;"    d
+OP_GENERIC_ONOFF_SET_UNACK     onoff-model.h   29;"    d
+OP_GENERIC_ONOFF_STATUS        onoff-model.h   30;"    d
+OP_MODEL_APP_BIND      config-model.h  80;"    d
+OP_MODEL_APP_GET       config-model.h  94;"    d
+OP_MODEL_APP_LIST      config-model.h  95;"    d
+OP_MODEL_APP_STATUS    config-model.h  81;"    d
+OP_MODEL_APP_UNBIND    config-model.h  82;"    d
+OP_NETKEY_ADD  config-model.h  83;"    d
+OP_NETKEY_DELETE       config-model.h  84;"    d
+OP_NETKEY_GET  config-model.h  85;"    d
+OP_NETKEY_LIST config-model.h  86;"    d
+OP_NETKEY_STATUS       config-model.h  87;"    d
+OP_NETKEY_UPDATE       config-model.h  88;"    d
+OP_NODE_IDENTITY_GET   config-model.h  89;"    d
+OP_NODE_IDENTITY_SET   config-model.h  90;"    d
+OP_NODE_IDENTITY_STATUS        config-model.h  91;"    d
+OP_NODE_RESET  config-model.h  92;"    d
+OP_NODE_RESET_STATUS   config-model.h  93;"    d
+OP_UNRELIABLE  util.h  28;"    d
+OP_VEND_MODEL_APP_GET  config-model.h  96;"    d
+OP_VEND_MODEL_APP_LIST config-model.h  97;"    d
+OUTPUT agent.h /^      OUTPUT,$/;"     e       enum:__anon2
+PKT_ACK        net.c   185;"   d       file:
+PKT_AKF_AID    net.c   169;"   d       file:
+PKT_CTL        net.c   151;"   d       file:
+PKT_DST        net.c   161;"   d       file:
+PKT_IVI        net.c   146;"   d       file:
+PKT_NID        net.c   149;"   d       file:
+PKT_OBO        net.c   173;"   d       file:
+PKT_OPCODE     net.c   171;"   d       file:
+PKT_SEGMENTED  net.c   166;"   d       file:
+PKT_SEQ        net.c   156;"   d       file:
+PKT_SEQ0       net.c   177;"   d       file:
+PKT_SRC        net.c   159;"   d       file:
+PKT_SZMIC      net.c   174;"   d       file:
+PKT_TRANS      net.c   163;"   d       file:
+PKT_TRANS_LEN  net.c   164;"   d       file:
+PKT_TTL        net.c   154;"   d       file:
+PRIMARY_ELEMENT_IDX    mesh-net.h      59;"    d
+PROMPT_OFF     main.c  68;"    d       file:
+PROMPT_ON      main.c  67;"    d       file:
+PROV_CAPS      prov.c  60;"    d       file:
+PROV_COMPLETE  prov.c  67;"    d       file:
+PROV_CONFIRM   prov.c  64;"    d       file:
+PROV_DATA      prov.c  66;"    d       file:
+PROV_ERR_CANT_ASSIGN_ADDR      prov.c  82;"    d       file:
+PROV_ERR_CONFIRM_FAILED        prov.c  78;"    d       file:
+PROV_ERR_DECRYPT_FAILED        prov.c  80;"    d       file:
+PROV_ERR_INSUF_RESOURCE        prov.c  79;"    d       file:
+PROV_ERR_INVALID_FORMAT        prov.c  76;"    d       file:
+PROV_ERR_INVALID_PDU   prov.c  75;"    d       file:
+PROV_ERR_UNEXPECTED_ERR        prov.c  81;"    d       file:
+PROV_ERR_UNEXPECTED_PDU        prov.c  77;"    d       file:
+PROV_FAILED    prov.c  68;"    d       file:
+PROV_INPUT_OOB prov.c  73;"    d       file:
+PROV_INP_CMPLT prov.c  63;"    d       file:
+PROV_INVITE    prov.c  59;"    d       file:
+PROV_NO_OOB    prov.c  70;"    d       file:
+PROV_OUTPUT_OOB        prov.c  72;"    d       file:
+PROV_PUB_KEY   prov.c  62;"    d       file:
+PROV_RANDOM    prov.c  65;"    d       file:
+PROV_START     prov.c  61;"    d       file:
+PROV_STATIC_OOB        prov.c  71;"    d       file:
+PROXIES_ADDRESS        mesh-net.h      48;"    d
+PROXY_CONFIG_PDU       mesh-net.h      27;"    d
+PROXY_FILTER_BLACKLIST mesh-net.h      114;"   d
+PROXY_FILTER_WHITELIST mesh-net.h      113;"   d
+PROXY_MESH_BEACON      mesh-net.h      26;"    d
+PROXY_NETWORK_PDU      mesh-net.h      25;"    d
+PROXY_OP_FILTER_ADD    mesh-net.h      108;"   d
+PROXY_OP_FILTER_DEL    mesh-net.h      109;"   d
+PROXY_OP_FILTER_STATUS mesh-net.h      110;"   d
+PROXY_OP_SET_FILTER_TYPE       mesh-net.h      107;"   d
+PROXY_PROVISIONING_PDU mesh-net.h      28;"    d
+RELAY  mesh-net.h      93;"    d
+RELAYS_ADDRESS mesh-net.h      50;"    d
+RELAY_HDR_SHIFT        mesh-net.h      94;"    d
+SEGMENTED      mesh-net.h      78;"    d
+SEGN_HDR_SHIFT mesh-net.h      104;"   d
+SEGO_HDR_SHIFT mesh-net.h      103;"   d
+SEG_HDR_SHIFT  mesh-net.h      80;"    d
+SEG_MASK       mesh-net.h      102;"   d
+SEG_MAX        mesh-net.h      63;"    d
+SEG_OFF        mesh-net.h      65;"    d
+SEG_TOTAL      mesh-net.h      105;"   d
+SEQ_MASK       mesh-net.h      32;"    d
+SEQ_ZERO_HDR_SHIFT     mesh-net.h      98;"    d
+SEQ_ZERO_MASK  mesh-net.h      97;"    d
+SET_PKT_ACK    net.c   186;"   d       file:
+SET_PKT_AKF_AID        net.c   170;"   d       file:
+SET_PKT_CTL    net.c   152;"   d       file:
+SET_PKT_DST    net.c   162;"   d       file:
+SET_PKT_IVI    net.c   147;"   d       file:
+SET_PKT_NID    net.c   150;"   d       file:
+SET_PKT_OPCODE net.c   172;"   d       file:
+SET_PKT_SEGMENTED      net.c   167;"   d       file:
+SET_PKT_SEGN   net.c   184;"   d       file:
+SET_PKT_SEGO   net.c   181;"   d       file:
+SET_PKT_SEQ    net.c   157;"   d       file:
+SET_PKT_SEQ0   net.c   178;"   d       file:
+SET_PKT_SRC    net.c   160;"   d       file:
+SET_PKT_SZMIC  net.c   175;"   d       file:
+SET_PKT_TTL    net.c   155;"   d       file:
+SET_TRANS_ACK  net.c   202;"   d       file:
+SET_TRANS_AKF_AID      net.c   195;"   d       file:
+SET_TRANS_OPCODE       net.c   193;"   d       file:
+SET_TRANS_SEGMENTD     net.c   190;"   d       file:
+SET_TRANS_SEQ0 net.c   199;"   d       file:
+SOL_ALG        crypto.c        38;"    d       file:
+SZMIC  mesh-net.h      95;"    d
+SZMIC_HDR_SHIFT        mesh-net.h      96;"    d
+TRANS_AKF      net.c   196;"   d       file:
+TRANS_AKF_AID  net.c   194;"   d       file:
+TRANS_LEN      net.c   207;"   d       file:
+TRANS_OPCODE   net.c   192;"   d       file:
+TRANS_PAYLOAD  net.c   206;"   d       file:
+TRANS_SEGMENTED        net.c   189;"   d       file:
+TRANS_SEGN     net.c   204;"   d       file:
+TRANS_SEGO     net.c   203;"   d       file:
+TRANS_SEQ0     net.c   198;"   d       file:
+TRANS_SZMIC    net.c   197;"   d       file:
+TTL_MASK       mesh-net.h      31;"    d
+UNASSIGNED_ADDRESS     mesh-net.h      47;"    d
+UNSEGMENTED    mesh-net.h      79;"    d
+VIRTUAL_ADDRESS_HIGH   mesh-net.h      53;"    d
+VIRTUAL_ADDRESS_LOW    mesh-net.h      52;"    d
+WHITELIST_FILTER       net.c   216;"   d       file:
+__packed       prov.c  /^typedef struct __packed {$/;" s       file:
+aad    net.c   /^      uint8_t         *aad;$/;"       m       struct:decrypt_params   file:
+access_pkt_decrypt     net.c   /^static uint16_t access_pkt_decrypt(uint8_t *nonce, uint8_t *aad,$/;"  f       file:
+access_rxed    net.c   /^static bool access_rxed(uint8_t *nonce, uint16_t net_idx,$/;" f       file:
+ack    net.c   /^      uint32_t        ack;$/;"        m       struct:mesh_sar_msg     file:
+ack_rxed       net.c   /^static bool ack_rxed(bool to, uint16_t src, uint16_t dst, bool obo,$/;"       f       file:
+ack_to net.c   /^      guint           ack_to;$/;"     m       struct:mesh_sar_msg     file:
+acquire_notify_reply   gatt.c  /^static void acquire_notify_reply(DBusMessage *message, void *user_data)$/;"   f       file:
+acquire_setup  gatt.c  /^static void acquire_setup(DBusMessageIter *iter, void *user_data)$/;" f       file:
+acquire_write_reply    gatt.c  /^static void acquire_write_reply(DBusMessage *message, void *user_data)$/;"    f       file:
+activity_cnt   net.c   /^      uint8_t         activity_cnt;$/;"       m       struct:mesh_sar_msg     file:
+adapter        main.c  /^struct adapter {$/;"  s       file:
+adapter_added  main.c  /^static void adapter_added(GDBusProxy *proxy)$/;"      f       file:
+adapter_removed        main.c  /^static void adapter_removed(GDBusProxy *proxy)$/;"    f       file:
+add_key        prov-db.c       /^static void add_key(json_object *jobject, const char *desc, uint8_t* key)$/;" f       file:
+add_node_idxs  prov-db.c       /^static void add_node_idxs(json_object *jnode, const char *desc,$/;"   f       file:
+addr16 node.h  /^              uint16_t addr16;$/;"    m       union:mesh_publication::__anon6
+address_pool   net.c   /^      GList *address_pool;$/;"        m       struct:mesh_net file:
+address_range  net.c   /^struct address_range$/;"      s       file:
+aes_cmac       crypto.c        /^static bool aes_cmac(int fd, const uint8_t *msg,$/;"  f       file:
+aes_cmac_N_start       crypto.c        /^static int aes_cmac_N_start(const uint8_t N[16])$/;"  f       file:
+aes_cmac_destroy       crypto.c        /^static void aes_cmac_destroy(int fd)$/;"      f       file:
+aes_cmac_one   crypto.c        /^static bool aes_cmac_one(const uint8_t key[16], const void *msg,$/;"  f       file:
+aes_cmac_setup crypto.c        /^static int aes_cmac_setup(const uint8_t key[16])$/;"  f       file:
+aes_ecb        crypto.c        /^static bool aes_ecb(int fd, const uint8_t plaintext[16], uint8_t encrypted[16])$/;"   f       file:
+aes_ecb_destroy        crypto.c        /^static void aes_ecb_destroy(int fd)$/;"       f       file:
+aes_ecb_one    crypto.c        /^static bool aes_ecb_one(const uint8_t key[16],$/;"    f       file:
+aes_ecb_setup  crypto.c        /^static int aes_ecb_setup(const uint8_t key[16])$/;"   f       file:
+agent_completion       agent.c /^bool agent_completion(void)$/;"       f
+agent_input_cb agent.h /^typedef void (*agent_input_cb)(oob_type_t type, void *input, uint16_t len,$/;"        t
+agent_input_request    agent.c /^bool agent_input_request(oob_type_t type, uint16_t max_len, agent_input_cb cb,$/;"    f
+agent_output_request   agent.c /^bool agent_output_request(const char* str)$/;"        f
+agent_output_request_cancel    agent.c /^void agent_output_request_cancel(void)$/;"    f
+akf_aid        net.c   /^      uint8_t         akf_aid;$/;"    m       struct:decrypt_params   file:
+akf_aid        net.c   /^      uint8_t         akf_aid;$/;"    m       struct:mesh_sar_msg     file:
+akf_aid        net.c   /^      uint8_t akf_aid;$/;"    m       struct:app_key_parts    file:
+alg_encrypt    crypto.c        /^static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,$/;"  f       file:
+alg_new        crypto.c        /^static int alg_new(int fd, const void *keyval, socklen_t keylen,$/;"  f       file:
+algorithm      prov.c  /^      uint8_t algorithm;$/;"  m       struct:__anon4  file:
+algorithms     prov.c  /^      uint16_t algorithms;$/;"        m       struct:__anon3  file:
+app_idx        net.c   /^      uint16_t        app_idx;$/;"    m       struct:decrypt_params   file:
+app_idx        node.h  /^      uint16_t app_idx;$/;"   m       struct:mesh_publication
+app_key_parts  net.c   /^struct app_key_parts {$/;"    s       file:
+app_keys       net.c   /^static GList *app_keys = NULL;$/;"    v       file:
+app_keys       node.c  /^      GList *app_keys;$/;"    m       struct:mesh_node        file:
+append_array_variant   main.c  /^static void append_array_variant(DBusMessageIter *iter, int type, void *val,$/;"      f       file:
+append_variant main.c  /^static void append_variant(DBusMessageIter *iter, int type, void *val)$/;"    f       file:
+attention      prov.c  /^      uint8_t attention;$/;"  m       struct:__packed file:
+auth_action    prov.c  /^      uint8_t auth_action;$/;"        m       struct:__anon4  file:
+auth_method    prov.c  /^      uint8_t auth_method;$/;"        m       struct:__anon4  file:
+auth_size      prov.c  /^      uint8_t auth_size;$/;"  m       struct:__anon4  file:
+beacon_key     net.c   /^      uint8_t beacon_key[16];$/;"     m       struct:net_key_parts    file:
+beacon_update  net.c   /^static void beacon_update(bool first, bool iv_update, uint32_t iv_index)$/;"  f       file:
+bind   node.h  /^      node_model_bind_callback bind;$/;"      m       struct:mesh_model_ops
+bindings       node.c  /^      GList *bindings;$/;"    m       struct:mesh_model       file:
+blacklist      net.c   /^      bool blacklist;$/;"     m       struct:mesh_net file:
+build_whitelist        net.c   /^struct build_whitelist {$/;"  s       file:
+calc_seqAuth   net.c   /^static uint32_t calc_seqAuth(uint32_t seq_num, uint8_t *trans)$/;"    f       file:
+caps   prov.c  /^      prov_caps       caps;$/;"       m       struct:__anon5  file:
+cb     agent.c /^      agent_input_cb cb;$/;"  m       struct:input_request    file:
+cb     gatt.c  /^      GDBusReturnFunction cb;$/;"     m       struct:notify_data      file:
+cb     gatt.c  /^      GDBusReturnFunction cb;$/;"     m       struct:write_data       file:
+cb     main.c  /^      GDBusReturnFunction cb;$/;"     m       struct:disconnect_data  file:
+cbs    node.c  /^      struct mesh_model_ops cbs;$/;"  m       struct:mesh_model       typeref:struct:mesh_model::mesh_model_ops       file:
+cfg_menu       config-client.c /^static const struct bt_shell_menu cfg_menu = {$/;"    v       typeref:struct:bt_shell_menu    file:
+char_is_mesh   main.c  /^static bool char_is_mesh(GDBusProxy *proxy, const char *target_uuid)$/;"      f       file:
+char_list      main.c  /^GList *char_list;$/;" v
+check_default_ctrl     main.c  /^static gboolean check_default_ctrl(void)$/;"  f       file:
+cid    node.h  /^      uint16_t cid;$/;"       m       struct:mesh_node_composition
+client_bind    onoff-model.c   /^static int client_bind(uint16_t app_idx, int action)$/;"      f       file:
+client_cbs     config-client.c /^static struct mesh_model_ops client_cbs = {$/;"       v       typeref:struct:mesh_model_ops   file:
+client_cbs     onoff-model.c   /^static struct mesh_model_ops client_cbs = {$/;"       v       typeref:struct:mesh_model_ops   file:
+client_msg_recvd       config-client.c /^static bool client_msg_recvd(uint16_t src, uint8_t *data,$/;" f       file:
+client_msg_recvd       onoff-model.c   /^static bool client_msg_recvd(uint16_t src, uint8_t *data,$/;" f       file:
+client_ready   main.c  /^static void client_ready(GDBusClient *client, void *user_data)$/;"    f       file:
+cmd_add_app_key        config-client.c /^static void cmd_add_app_key(int argc, char *argv[])$/;"       f       file:
+cmd_add_net_key        config-client.c /^static void cmd_add_net_key(int argc, char *argv[])$/;"       f       file:
+cmd_app_key    config-client.c /^static void cmd_app_key(int argc, char *argv[], uint32_t opcode)$/;"  f       file:
+cmd_bind       config-client.c /^static void cmd_bind(int argc, char *argv[])$/;"      f       file:
+cmd_connect    main.c  /^static void cmd_connect(int argc, char *argv[])$/;"   f       file:
+cmd_default    config-client.c /^static void cmd_default(uint32_t opcode)$/;"  f       file:
+cmd_del_app_key        config-client.c /^static void cmd_del_app_key(int argc, char *argv[])$/;"       f       file:
+cmd_del_net_key        config-client.c /^static void cmd_del_net_key(int argc, char *argv[])$/;"       f       file:
+cmd_disconn    main.c  /^static void cmd_disconn(int argc, char *argv[])$/;"   f       file:
+cmd_get_app    config-client.c /^static void cmd_get_app(int argc, char *argv[])$/;"   f       file:
+cmd_get_composition    config-client.c /^static void cmd_get_composition(int argc, char *argv[])$/;"   f       file:
+cmd_get_ident  config-client.c /^static void cmd_get_ident(int argc, char *argv[])$/;" f       file:
+cmd_get_proxy  config-client.c /^static void cmd_get_proxy(int argc, char *argv[])$/;" f       file:
+cmd_get_pub    config-client.c /^static void cmd_get_pub(int argc, char *argv[])$/;"   f       file:
+cmd_get_relay  config-client.c /^static void cmd_get_relay(int argc, char *argv[])$/;" f       file:
+cmd_get_status onoff-model.c   /^static void cmd_get_status(int argc, char *argv[])$/;"        f       file:
+cmd_get_ttl    config-client.c /^static void cmd_get_ttl(int argc, char *argv[])$/;"   f       file:
+cmd_info       main.c  /^static void cmd_info(int argc, char *argv[])$/;"      f       file:
+cmd_list       main.c  /^static void cmd_list(int argc, char *argv[])$/;"      f       file:
+cmd_net_key    config-client.c /^static void cmd_net_key(int argc, char *argv[], uint32_t opcode)$/;"  f       file:
+cmd_power      main.c  /^static void cmd_power(int argc, char *argv[])$/;"     f       file:
+cmd_print_local        main.c  /^ static void cmd_print_local(int argc, char *argv[])$/;"      f       file:
+cmd_print_mesh main.c  /^static void cmd_print_mesh(int argc, char *argv[])$/;"        f       file:
+cmd_scan_unprovisioned main.c  /^static void cmd_scan_unprovisioned(int argc, char *argv[])$/;"        f       file:
+cmd_security   main.c  /^static void cmd_security(int argc, char *argv[])$/;"  f       file:
+cmd_select     main.c  /^static void cmd_select(int argc, char *argv[])$/;"    f       file:
+cmd_set        onoff-model.c   /^static void cmd_set(int argc, char *argv[])$/;"       f       file:
+cmd_set_hb     config-client.c /^static void cmd_set_hb(int argc, char *argv[])$/;"    f       file:
+cmd_set_ident  config-client.c /^static void cmd_set_ident(int argc, char *argv[])$/;" f       file:
+cmd_set_node   config-client.c /^static void cmd_set_node(int argc, char *argv[])$/;"  f       file:
+cmd_set_node   onoff-model.c   /^static void cmd_set_node(int argc, char *argv[])$/;"  f       file:
+cmd_set_proxy  config-client.c /^static void cmd_set_proxy(int argc, char *argv[])$/;" f       file:
+cmd_set_pub    config-client.c /^static void cmd_set_pub(int argc, char *argv[])$/;"   f       file:
+cmd_set_relay  config-client.c /^static void cmd_set_relay(int argc, char *argv[])$/;" f       file:
+cmd_set_ttl    config-client.c /^static void cmd_set_ttl(int argc, char *argv[])$/;"   f       file:
+cmd_show       main.c  /^static void cmd_show(int argc, char *argv[])$/;"      f       file:
+cmd_start_prov main.c  /^static void cmd_start_prov(int argc, char *argv[])$/;"        f       file:
+cmd_sub_add    config-client.c /^static void cmd_sub_add(int argc, char *argv[])$/;"   f       file:
+cmd_sub_get    config-client.c /^static void cmd_sub_get(int argc, char *argv[])$/;"   f       file:
+cnt    net.c   /^      uint16_t        cnt;$/;"        m       struct:mesh_destination file:
+comp   node.c  /^      struct mesh_node_composition *comp;$/;" m       struct:mesh_node        typeref:struct:mesh_node::mesh_node_composition file:
+conf_in        prov.c  /^      conf_input              conf_in;$/;"    m       struct:prov_data        file:
+conf_input     prov.c  /^} __attribute__ ((packed))    conf_input;$/;" t       typeref:struct:__anon5  file:
+conf_key       prov.c  /^      uint8_t                 conf_key[16];$/;"       m       struct:prov_data        file:
+config_client_get_composition  config-client.c /^void config_client_get_composition(uint32_t dst)$/;"  f
+config_client_init     config-client.c /^bool config_client_init(void)$/;"     f
+config_send    config-client.c /^static bool config_send(uint8_t *buf, uint16_t len)$/;"       f       file:
+config_server_init     config-server.c /^bool config_server_init(void)$/;"     f
+connect_handler        main.c  /^static void connect_handler(DBusConnection *connection, void *user_data)$/;"  f       file:
+connect_reply  main.c  /^static void connect_reply(DBusMessage *message, void *user_data)$/;"  f       file:
+connection     main.c  /^} connection;$/;"     v       typeref:struct:__anon1
+credential     node.h  /^      uint8_t credential;$/;" m       struct:mesh_publication
+crpl   node.h  /^      uint16_t crpl;$/;"      m       struct:mesh_node_composition
+crypto_128     crypto.c        /^static bool crypto_128(const uint8_t n[16], const char *s, uint8_t out128[16])$/;"    f       file:
+ctl    net.c   /^      bool            ctl;$/;"        m       struct:mesh_sar_msg     file:
+ctl_rxed       net.c   /^static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,$/;" f       file:
+ctrl_list      main.c  /^static GList *ctrl_list;$/;"  v       file:
+current        net.c   /^      struct app_key_parts    current;$/;"    m       struct:mesh_app_key     typeref:struct:mesh_app_key::app_key_parts      file:
+current        net.c   /^      struct net_key_parts    current;$/;"    m       struct:mesh_net_key     typeref:struct:mesh_net_key::net_key_parts      file:
+data   main.c  /^      void *data;$/;" m       struct:disconnect_data  file:
+data   net.c   /^      uint8_t         data[20]; \/* Open ended, min 20 *\/$/;"        m       struct:mesh_sar_msg     file:
+data   net.c   /^      uint8_t         data[30];$/;"   m       struct:mesh_pkt file:
+data   net.c   /^      uint8_t data[12];$/;"   m       struct:build_whitelist  file:
+data_in        main.c  /^      GDBusProxy *data_in;$/;"        m       struct:__anon1  file:
+data_out       main.c  /^      GDBusProxy *data_out;$/;"       m       struct:__anon1  file:
+data_out_notify        main.c  /^static void data_out_notify(GDBusProxy *proxy, bool enable,$/;"       f       file:
+dbus_conn      main.c  /^static DBusConnection *dbus_conn;$/;" v       file:
+decode_params  net.c   /^struct decode_params {$/;"    s       file:
+decrypt_params net.c   /^struct decrypt_params {$/;"   s       file:
+default_ctrl   main.c  /^static struct adapter *default_ctrl;$/;"      v       typeref:struct:adapter  file:
+default_ttl    net.c   /^      uint8_t default_ttl;$/;"        m       struct:mesh_net file:
+delete_key     net.c   /^static bool delete_key(GList **list, uint16_t index)$/;"      f       file:
+deliver_model_data     node.c  /^static bool deliver_model_data(struct mesh_element* element, uint16_t src,$/;"        f       file:
+dest   net.c   /^      GList *dest;    \/* List of valid local destinations for Whitelist *\/$/;"      m       struct:mesh_net file:
+dev_key        node.c  /^      uint8_t dev_key[16];$/;"        m       struct:mesh_node        file:
+dev_key        prov.c  /^      uint8_t                 dev_key[16];$/;"        m       struct:prov_data        file:
+dev_pub_key    prov.c  /^      uint8_t dev_pub_key[64];$/;"    m       struct:__anon5  file:
+dev_uuid       main.c  /^      uint8_t dev_uuid[16];$/;"       m       struct:__anon1  file:
+dev_uuid       main.c  /^      uint8_t dev_uuid[16];$/;"       m       struct:mesh_device      file:
+dev_uuid       node.c  /^      uint8_t dev_uuid[16];$/;"       m       struct:mesh_node        file:
+dev_uuid       node.h  /^      uint8_t dev_uuid[16];$/;"       m       struct:prov_svc_data
+device main.c  /^      GDBusProxy *device;$/;" m       struct:__anon1  file:
+device_is_child        main.c  /^static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master)$/;"   f       file:
+device_removed main.c  /^static void device_removed(GDBusProxy *proxy)$/;"     f       file:
+dict_append_array      main.c  /^static void dict_append_array(DBusMessageIter *dict, const char *key, int type,$/;"   f       file:
+dict_append_basic_array        main.c  /^static void dict_append_basic_array(DBusMessageIter *dict, int key_type,$/;"  f       file:
+dict_append_entry      main.c  /^static void dict_append_entry(DBusMessageIter *dict, const char *key,$/;"     f       file:
+disc_notify_cb main.c  /^static void disc_notify_cb(DBusMessage *message, void *user_data)$/;" f       file:
+disconn_reply  main.c  /^static void disconn_reply(DBusMessage *message, void *user_data)$/;"  f       file:
+disconnect     main.c  /^static void disconnect(GDBusReturnFunction cb, void *user_data)$/;"   f       file:
+disconnect_data        main.c  /^struct disconnect_data {$/;"  s       file:
+disconnect_device      main.c  /^static void disconnect_device(GDBusReturnFunction cb, void *user_data)$/;"    f       file:
+disconnect_handler     main.c  /^static void disconnect_handler(DBusConnection *connection, void *user_data)$/;"       f       file:
+discover_mesh  main.c  /^static bool discover_mesh;$/;"        v       file:
+discovering    main.c  /^static bool discovering = false;$/;"  v       file:
+dst    net.c   /^      uint16_t        dst;$/;"        m       struct:decrypt_params   file:
+dst    net.c   /^      uint16_t        dst;$/;"        m       struct:mesh_destination file:
+dst    net.c   /^      uint32_t        dst;$/;"        m       struct:mesh_sar_msg     file:
+duplicate      main.c  /^      dbus_bool_t duplicate;$/;"      m       struct:set_discovery_filter_args        file:
+ecdh_secret    prov.c  /^      uint8_t                 ecdh_secret[32];$/;"    m       struct:prov_data        file:
+element_free   node.c  /^static void element_free(void *data)$/;"      f       file:
+elements       node.c  /^      GList *elements;$/;"    m       struct:mesh_node        file:
+enable gatt.c  /^      bool enable;$/;"        m       struct:notify_data      file:
+enc_key        net.c   /^      uint8_t enc_key[16];$/;"        m       struct:net_key_parts    file:
+eph_priv_key   prov.c  /^      uint8_t                 eph_priv_key[32];$/;"   m       struct:prov_data        file:
+expected_pdu_size      prov.c  /^static const uint16_t expected_pdu_size[] = {$/;"     v       file:
+features       node.c  /^      uint16_t features;$/;"  m       struct:mesh_node        file:
+filtered_scan_pathloss main.c  /^static gint filtered_scan_pathloss = DISTANCE_VAL_INVALID;$/;"        v       file:
+filtered_scan_rssi     main.c  /^static gint filtered_scan_rssi = DISTANCE_VAL_INVALID;$/;"    v       file:
+filtered_scan_transport        main.c  /^static char *filtered_scan_transport = "le";$/;"      v       file:
+filtered_scan_uuids    main.c  /^static char **filtered_scan_uuids;$/;"        v       file:
+filtered_scan_uuids_len        main.c  /^static size_t filtered_scan_uuids_len;$/;"    v       file:
+find_app_key_by_idx    net.c   /^static struct mesh_app_key *find_app_key_by_idx(uint16_t app_idx)$/;" f       file:
+find_configured_model  prov-db.c       /^static json_object* find_configured_model(struct mesh_node *node, int ele_idx,$/;"    f       file:
+find_ctrl_by_address   main.c  /^static struct adapter *find_ctrl_by_address(GList *source, const char *address)$/;"   f       file:
+find_device_by_proxy   main.c  /^static struct mesh_device *find_device_by_proxy(GList *source,$/;"    f       file:
+find_device_by_uuid    main.c  /^static struct mesh_device *find_device_by_uuid(GList *source, uint8_t uuid[16])$/;"   f       file:
+find_net_key_by_id     net.c   /^static struct mesh_net_key *find_net_key_by_id(const uint8_t *net_id)$/;"     f       file:
+find_net_key_by_idx    net.c   /^static struct mesh_net_key *find_net_key_by_idx(uint16_t net_idx)$/;" f       file:
+find_node_by_primary   prov-db.c       /^static json_object *find_node_by_primary(json_object *jmain, uint16_t primary)$/;"    f       file:
+find_parent    main.c  /^static struct adapter *find_parent(GDBusProxy *device)$/;"    f       file:
+find_sar_in_by_src     net.c   /^static struct mesh_sar_msg *find_sar_in_by_src(uint16_t src)$/;"      f       file:
+find_sar_out_by_dst    net.c   /^static struct mesh_sar_msg *find_sar_out_by_dst(uint16_t dst)$/;"     f       file:
+find_virt_by_dst       net.c   /^static struct mesh_virt_addr *find_virt_by_dst(uint32_t dst)$/;"      f       file:
+flush_pkt_list net.c   /^static void flush_pkt_list(GList **list)$/;"  f       file:
+flush_sar      net.c   /^static void flush_sar(GList **list, struct mesh_sar_msg *sar)$/;"     f       file:
+flush_sar_list net.c   /^static void flush_sar_list(GList **list)$/;"  f       file:
+forget_mesh_devices    main.c  /^static void forget_mesh_devices()$/;" f       file:
+free_node_resources    node.c  /^static void free_node_resources(void *data)$/;"       f       file:
+gatt_data      gatt.c  /^      uint8_t *gatt_data;$/;" m       struct:write_data       file:
+gatt_len       gatt.c  /^      uint8_t gatt_len;$/;"   m       struct:write_data       file:
+generic        net.c   /^      struct generic_key      generic;$/;"    m       struct:mesh_app_key     typeref:struct:mesh_app_key::generic_key        file:
+generic        net.c   /^      struct generic_key      generic;$/;"    m       struct:mesh_net_key     typeref:struct:mesh_net_key::generic_key        file:
+generic_callback       main.c  /^static void generic_callback(const DBusError *error, void *user_data)$/;"     f       file:
+generic_key    net.c   /^struct generic_key {$/;"      s       file:
+get_characteristic     main.c  /^static GDBusProxy *get_characteristic(GDBusProxy *device, const char *char_uuid)$/;"  f       file:
+get_characteristic_value       main.c  /^static int get_characteristic_value(DBusMessageIter *value, uint8_t *buf)$/;" f       file:
+get_enc_keys   net.c   /^static bool get_enc_keys(uint16_t app_idx, uint16_t dst,$/;"  f       file:
+get_jmodel_obj prov-db.c       /^static json_object *get_jmodel_obj(struct mesh_node *node, uint8_t ele_idx,$/;"       f       file:
+get_key        net.c   /^static uint8_t *get_key(GList *list, uint16_t index)$/;"      f       file:
+get_model      node.c  /^static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,$/;"      f       file:
+get_next_seq   net.c   /^static uint32_t get_next_seq()$/;"    f       file:
+get_sequence_number    net.c   /^uint32_t get_sequence_number(void)$/;"        f
+help   main.c  /^static const char *help[] = {$/;"     v       file:
+hex2str        util.c  /^size_t hex2str(uint8_t *in, size_t in_len, char *out,$/;"     f
+hide   main.c  /^      gboolean hide;$/;"      m       struct:mesh_device      file:
+id     node.c  /^      uint32_t id;$/;"        m       struct:mesh_model       file:
+identity_calc  crypto.c        /^static bool identity_calc(const uint8_t net_key[16], uint16_t addr,$/;"       f       file:
+idx    net.c   /^      uint16_t        idx;$/;"        m       struct:generic_key      file:
+in_action      prov.c  /^char *in_action[3] = {$/;"    v
+index  node.c  /^      uint8_t index;$/;"      m       struct:mesh_element     file:
+input_action   prov.c  /^      uint16_t input_action;$/;"      m       struct:__anon3  file:
+input_request  agent.c /^struct input_request {$/;"    s       file:
+input_size     prov.c  /^      uint8_t  input_size;$/;"        m       struct:__anon3  file:
+invite prov.c  /^      prov_invite     invite;$/;"     m       struct:__anon5  file:
+iov    gatt.c  /^      struct iovec iov;$/;"   m       struct:write_data       typeref:struct:write_data::iovec        file:
+iv_index       net.c   /^      uint32_t                iv_index;$/;"   m       struct:decode_params    file:
+iv_index       net.c   /^      uint32_t        iv_index;$/;"   m       struct:decrypt_params   file:
+iv_index       net.c   /^      uint32_t        iv_index;$/;"   m       struct:mesh_sar_msg     file:
+iv_index       net.c   /^      uint32_t iv_index;$/;"  m       struct:mesh_net file:
+iv_index       node.c  /^      uint32_t iv_index;$/;"  m       struct:mesh_node        file:
+iv_upd_state   net.c   /^      uint8_t iv_upd_state;$/;"       m       struct:mesh_net file:
+iv_update      net.c   /^      bool iv_update;$/;"     m       struct:mesh_net file:
+iv_update_timeout      net.c   /^      guint iv_update_timeout;$/;"    m       struct:mesh_net file:
+key    net.c   /^      uint8_t key[16];$/;"    m       struct:app_key_parts    file:
+keys_app_key_add       net.c   /^bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,$/;"   f
+keys_app_key_delete    net.c   /^bool keys_app_key_delete(uint16_t app_idx)$/;"        f
+keys_app_key_get       net.c   /^uint8_t *keys_app_key_get(uint16_t app_idx, bool current)$/;" f
+keys_app_key_get_bound net.c   /^uint16_t keys_app_key_get_bound(uint16_t app_idx)$/;" f
+keys_cleanup_all       net.c   /^void keys_cleanup_all(void)$/;"       f
+keys_get_kr_phase      net.c   /^uint8_t keys_get_kr_phase(uint16_t net_idx)$/;"       f
+keys_net_key_add       net.c   /^bool keys_net_key_add(uint16_t net_idx, uint8_t *key, bool update)$/;"        f
+keys_net_key_delete    net.c   /^bool keys_net_key_delete(uint16_t net_idx)$/;"        f
+keys_net_key_get       net.c   /^uint8_t *keys_net_key_get(uint16_t net_idx, bool current)$/;" f
+keys_set_kr_phase      net.c   /^bool keys_set_kr_phase(uint16_t index, uint8_t phase)$/;"     f
+len    agent.c /^      uint16_t len;$/;"       m       struct:input_request    file:
+len    net.c   /^      uint16_t        len;$/;"        m       struct:decrypt_params   file:
+len    net.c   /^      uint16_t        len;$/;"        m       struct:mesh_sar_msg     file:
+len    net.c   /^      uint8_t         len;$/;"        m       struct:mesh_pkt file:
+len    net.c   /^      uint8_t len;$/;"        m       struct:build_whitelist  file:
+loc    node.c  /^      uint16_t loc;$/;"       m       struct:mesh_element     file:
+local_filename prov-db.c       /^static const char *local_filename;$/;"        v       file:
+local_node     node.c  /^static struct mesh_node *local_node;$/;"      v       typeref:struct:mesh_node        file:
+lpn    node.h  /^      bool lpn;$/;"   m       struct:mesh_node_composition
+main   main.c  /^int main(int argc, char *argv[])$/;"  f
+main_menu      main.c  /^static const struct bt_shell_menu main_menu = {$/;"   v       typeref:struct:bt_shell_menu    file:
+main_menu      main.c  /^static const struct bt_shell_menu main_menu;$/;"      v       typeref:struct:bt_shell_menu    file:
+match_address_range    net.c   /^static int match_address_range(const void *a, const void *b)$/;"      f       file:
+match_destination      net.c   /^static int match_destination(const void *a, const void *b)$/;"        f       file:
+match_device_uuid      node.c  /^static int match_device_uuid(const void *a, const void *b)$/;"        f       file:
+match_element_idx      node.c  /^static int match_element_idx(const void *a, const void *b)$/;"        f       file:
+match_key_index        net.c   /^static int match_key_index(const void *a, const void *b)$/;"  f       file:
+match_model_id node.c  /^static int match_model_id(const void *a, const void *b)$/;"   f       file:
+match_net_id   net.c   /^static int match_net_id(const void *a, const void *net_id)$/;"        f       file:
+match_node_unicast     node.c  /^static int match_node_unicast(const void *a, const void *b)$/;"       f       file:
+match_sar_dst  net.c   /^static int match_sar_dst(const void *a, const void *b)$/;"    f       file:
+match_sar_src  net.c   /^static int match_sar_src(const void *a, const void *b)$/;"    f       file:
+match_virt_dst net.c   /^static int match_virt_dst(const void *a, const void *b)$/;"   f       file:
+max    net.c   /^      uint16_t max;$/;"       m       struct:address_range    file:
+mesh_app_key   net.c   /^struct mesh_app_key {$/;"     s       file:
+mesh_conf      prov.c  /^      uint8_t                 mesh_conf[16];$/;"      m       struct:prov_data        file:
+mesh_config_dir        main.c  /^static const char *mesh_config_dir;$/;"       v       file:
+mesh_crypto_aes_ccm_decrypt    crypto.c        /^bool mesh_crypto_aes_ccm_decrypt(const uint8_t nonce[13], const uint8_t key[16],$/;"  f
+mesh_crypto_aes_ccm_encrypt    crypto.c        /^bool mesh_crypto_aes_ccm_encrypt(const uint8_t nonce[13], const uint8_t key[16],$/;"  f
+mesh_crypto_aes_cmac   crypto.c        /^bool mesh_crypto_aes_cmac(const uint8_t key[16], const uint8_t *msg,$/;"      f
+mesh_crypto_application_decrypt        crypto.c        /^bool mesh_crypto_application_decrypt(uint8_t key_id, uint32_t seq, uint16_t src,$/;"  f
+mesh_crypto_application_encrypt        crypto.c        /^bool mesh_crypto_application_encrypt(uint8_t key_id, uint32_t seq, uint16_t src,$/;"  f
+mesh_crypto_application_nonce  crypto.c        /^bool mesh_crypto_application_nonce(uint32_t seq, uint16_t src,$/;"    f
+mesh_crypto_beacon_cmac        crypto.c        /^bool mesh_crypto_beacon_cmac(const uint8_t encryption_key[16],$/;"    f
+mesh_crypto_device_key crypto.c        /^bool mesh_crypto_device_key(const uint8_t secret[32],$/;"     f
+mesh_crypto_device_nonce       crypto.c        /^bool mesh_crypto_device_nonce(uint32_t seq, uint16_t src,$/;" f
+mesh_crypto_identity   crypto.c        /^bool mesh_crypto_identity(const uint8_t net_key[16], uint16_t addr,$/;"       f
+mesh_crypto_identity_check     crypto.c        /^bool mesh_crypto_identity_check(const uint8_t net_key[16], uint16_t addr,$/;" f
+mesh_crypto_k1 crypto.c        /^bool mesh_crypto_k1(const uint8_t ikm[16], const uint8_t salt[16],$/;"        f
+mesh_crypto_k2 crypto.c        /^bool mesh_crypto_k2(const uint8_t n[16], const uint8_t *p, size_t p_len,$/;"  f
+mesh_crypto_k3 crypto.c        /^bool mesh_crypto_k3(const uint8_t n[16], uint8_t out64[8])$/;"        f
+mesh_crypto_k4 crypto.c        /^bool mesh_crypto_k4(const uint8_t a[16], uint8_t out6[1])$/;" f
+mesh_crypto_network_decrypt    crypto.c        /^bool mesh_crypto_network_decrypt(bool ctl, uint8_t ttl,$/;"   f
+mesh_crypto_network_encrypt    crypto.c        /^bool mesh_crypto_network_encrypt(bool ctl, uint8_t ttl,$/;"   f
+mesh_crypto_network_nonce      crypto.c        /^bool mesh_crypto_network_nonce(bool ctl, uint8_t ttl, uint32_t seq,$/;"       f
+mesh_crypto_nkbk       crypto.c        /^bool mesh_crypto_nkbk(const uint8_t n[16], uint8_t beacon_key[16])$/;"        f
+mesh_crypto_nkik       crypto.c        /^bool mesh_crypto_nkik(const uint8_t n[16], uint8_t identity_key[16])$/;"      f
+mesh_crypto_nonce      crypto.c        /^bool mesh_crypto_nonce(const uint8_t secret[32],$/;"  f
+mesh_crypto_packet_decode      crypto.c        /^bool mesh_crypto_packet_decode(const uint8_t *packet, uint8_t packet_len,$/;" f
+mesh_crypto_packet_encode      crypto.c        /^bool mesh_crypto_packet_encode(uint8_t *packet, uint8_t packet_len,$/;"       f
+mesh_crypto_prov_conf_key      crypto.c        /^bool mesh_crypto_prov_conf_key(const uint8_t secret[32],$/;"  f
+mesh_crypto_prov_prov_salt     crypto.c        /^bool mesh_crypto_prov_prov_salt(const uint8_t conf_salt[16],$/;"      f
+mesh_crypto_s1 crypto.c        /^bool mesh_crypto_s1(const void *info, size_t len, uint8_t salt[16])$/;"       f
+mesh_crypto_session_key        crypto.c        /^bool mesh_crypto_session_key(const uint8_t secret[32],$/;"    f
+mesh_crypto_virtual_addr       crypto.c        /^bool mesh_crypto_virtual_addr(const uint8_t virtual_label[16],$/;"    f
+mesh_destination       net.c   /^struct mesh_destination {$/;" s       file:
+mesh_device    main.c  /^struct mesh_device {$/;"      s       file:
+mesh_devices   main.c  /^      GList *mesh_devices;$/;"        m       struct:adapter  file:
+mesh_element   node.c  /^struct mesh_element {$/;"     s       file:
+mesh_gatt_is_child     gatt.c  /^bool mesh_gatt_is_child(GDBusProxy *proxy, GDBusProxy *parent,$/;"    f
+mesh_gatt_notify       gatt.c  /^bool mesh_gatt_notify(GDBusProxy *proxy, bool enable, GDBusReturnFunction cb,$/;"     f
+mesh_gatt_sar  gatt.c  /^uint16_t mesh_gatt_sar(uint8_t **pkt, uint16_t size)$/;"      f
+mesh_gatt_write        gatt.c  /^bool mesh_gatt_write(GDBusProxy *proxy, uint8_t *buf, uint16_t len,$/;"       f
+mesh_get_random_bytes  crypto.c        /^bool mesh_get_random_bytes(void *buf, size_t num_bytes)$/;"   f
+mesh_local_config_filename     main.c  /^static char *mesh_local_config_filename;$/;"  v       file:
+mesh_model     node.c  /^struct mesh_model {$/;"       s       file:
+mesh_model_ops node.h  /^struct mesh_model_ops {$/;"   s
+mesh_net       net.c   /^struct mesh_net {$/;" s       file:
+mesh_net_key   net.c   /^struct mesh_net_key {$/;"     s       file:
+mesh_node      node.c  /^struct mesh_node {$/;"        s       file:
+mesh_node_composition  node.h  /^struct mesh_node_composition {$/;"    s
+mesh_opcode_get        util.c  /^bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n)$/;"   f
+mesh_opcode_set        util.c  /^uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf)$/;"   f
+mesh_pkt       net.c   /^struct mesh_pkt {$/;" s       file:
+mesh_prov_db_filename  main.c  /^static char *mesh_prov_db_filename;$/;"       v       file:
+mesh_prov_done main.c  /^static void mesh_prov_done(void *user_data, int status)$/;"   f       file:
+mesh_publication       node.h  /^struct mesh_publication {$/;" s
+mesh_sar_msg   net.c   /^struct mesh_sar_msg {$/;"     s       file:
+mesh_session_setup     main.c  /^static void mesh_session_setup(GDBusProxy *proxy)$/;" f       file:
+mesh_status_str        util.c  /^const char *mesh_status_str(uint8_t status)$/;"       f
+mesh_virt_addr net.c   /^struct mesh_virt_addr {$/;"   s       file:
+message_handler        main.c  /^static void message_handler(DBusConnection *connection,$/;"   f       file:
+min    net.c   /^      uint16_t min;$/;"       m       struct:address_range    file:
+model_free     node.c  /^static void model_free(void *data)$/;"        f       file:
+models node.c  /^      GList *models;$/;"      m       struct:mesh_element     file:
+msg_out        net.c   /^      GList *msg_out; \/* Pre-Network encoded, might be multi-segment *\/$/;" m       struct:mesh_net file:
+msg_rxed       net.c   /^static bool msg_rxed(uint16_t net_idx, uint32_t iv_index, bool szmic,$/;"     f       file:
+msg_to net.c   /^      guint           msg_to;$/;"     m       struct:mesh_sar_msg     file:
+name   node.c  /^      const char *name;$/;"   m       struct:mesh_node        file:
+net    net.c   /^static struct mesh_net net;$/;"       v       typeref:struct:mesh_net file:
+net_access_layer_send  net.c   /^bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,$/;"       f
+net_add_address_pool   net.c   /^bool net_add_address_pool(uint16_t min, uint16_t max)$/;"     f
+net_ctl_msg_send       net.c   /^bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,$/;"    f
+net_data_ready net.c   /^bool net_data_ready(uint8_t *msg, uint8_t len)$/;"    f
+net_dest_ref   net.c   /^void net_dest_ref(uint16_t dst)$/;"   f
+net_dest_unref net.c   /^void net_dest_unref(uint16_t dst)$/;" f
+net_get_default_ttl    net.c   /^uint8_t net_get_default_ttl()$/;"     f
+net_get_flags  net.c   /^bool net_get_flags(uint16_t net_idx, uint8_t *out_flags)$/;"  f
+net_get_iv_index       net.c   /^uint32_t net_get_iv_index(bool *update)$/;"   f
+net_get_key    net.c   /^bool net_get_key(uint16_t net_idx, uint8_t *key)$/;"  f
+net_get_seq_num        net.c   /^uint32_t net_get_seq_num()$/;"        f
+net_id net.c   /^      uint8_t net_id[8];$/;"  m       struct:net_key_parts    file:
+net_idx        main.c  /^      uint16_t net_idx;$/;"   m       struct:__anon1  file:
+net_idx        net.c   /^      uint16_t                net_idx;$/;"    m       struct:mesh_app_key     file:
+net_idx        net.c   /^      uint16_t        net_idx;$/;"    m       struct:decrypt_params   file:
+net_idx        net.c   /^      uint16_t        net_idx;$/;"    m       struct:mesh_sar_msg     file:
+net_idx        prov.c  /^      uint16_t                net_idx;$/;"    m       struct:prov_data        file:
+net_key        net.c   /^      struct mesh_net_key     *net_key;$/;"   m       struct:decode_params    typeref:struct:decode_params::mesh_net_key      file:
+net_key        net.c   /^      uint8_t net_key[16];$/;"        m       struct:net_key_parts    file:
+net_key_parts  net.c   /^struct net_key_parts {$/;"    s       file:
+net_keys       net.c   /^static GList *net_keys = NULL;$/;"    v       file:
+net_keys       node.c  /^      GList *net_keys;$/;"    m       struct:mesh_node        file:
+net_mesh_session_open_callback net.h   /^typedef void (*net_mesh_session_open_callback)(int status);$/;"       t
+net_obtain_address     net.c   /^uint16_t net_obtain_address(uint8_t num_eles)$/;"     f
+net_packet_decode      net.c   /^static struct mesh_net_key *net_packet_decode(bool proxy, uint32_t iv_index,$/;"      f       file:
+net_register_group     net.c   /^bool net_register_group(uint16_t group_addr)$/;"      f
+net_register_unicast   net.c   /^bool net_register_unicast(uint16_t unicast, uint8_t count)$/;"        f
+net_register_virtual   net.c   /^uint32_t net_register_virtual(uint8_t buf[16])$/;"    f
+net_release_address    net.c   /^void net_release_address(uint16_t addr, uint8_t num_elements)$/;"     f
+net_reserve_address_range      net.c   /^bool net_reserve_address_range(uint16_t base, uint8_t num_elements)$/;"       f
+net_session_close      net.c   /^void net_session_close(GDBusProxy *data_in)$/;"       f
+net_session_open       net.c   /^bool net_session_open(GDBusProxy *data_in, bool provisioner,$/;"      f
+net_set_default_ttl    net.c   /^bool net_set_default_ttl(uint8_t ttl)$/;"     f
+net_set_iv_index       net.c   /^void net_set_iv_index(uint32_t iv_index, bool update)$/;"     f
+net_set_seq_num        net.c   /^bool net_set_seq_num(uint32_t seq_num)$/;"    f
+net_validate_proxy_beacon      net.c   /^uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon)$/;"   f
+new    net.c   /^      struct app_key_parts    new;$/;"        m       struct:mesh_app_key     typeref:struct:mesh_app_key::app_key_parts      file:
+new    net.c   /^      struct net_key_parts    new;$/;"        m       struct:mesh_net_key     typeref:struct:mesh_net_key::net_key_parts      file:
+new_addr       prov.c  /^      uint16_t                new_addr;$/;"   m       struct:prov_data        file:
+nid    net.c   /^      uint8_t nid;$/;"        m       struct:net_key_parts    file:
+node_add_binding       node.c  /^bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,$/;"    f
+node_app_key_add       node.c  /^bool node_app_key_add(struct mesh_node *node, uint16_t idx)$/;"       f
+node_app_key_delete    node.c  /^bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,$/;"        f
+node_cleanup   node.c  /^void node_cleanup(void)$/;"   f
+node_create_new        node.c  /^struct mesh_node *node_create_new(struct prov_svc_data *prov)$/;"     f
+node_find_by_addr      node.c  /^struct mesh_node *node_find_by_addr(uint16_t addr)$/;"        f
+node_find_by_uuid      node.c  /^struct mesh_node *node_find_by_uuid(uint8_t uuid[16])$/;"     f
+node_free      node.c  /^void node_free(struct mesh_node *node)$/;"    f
+node_get_app_keys      node.c  /^GList *node_get_app_keys(struct mesh_node *node)$/;"  f
+node_get_composition   node.c  /^struct mesh_node_composition *node_get_composition(struct mesh_node *node)$/;"        f
+node_get_default_ttl   node.c  /^uint8_t node_get_default_ttl(struct mesh_node *node)$/;"      f
+node_get_device_key    node.c  /^uint8_t *node_get_device_key(struct mesh_node *node)$/;"      f
+node_get_iv_index      node.c  /^uint32_t node_get_iv_index(struct mesh_node *node)$/;"        f
+node_get_local_node    node.c  /^struct mesh_node *node_get_local_node()$/;"   f
+node_get_net_keys      node.c  /^GList *node_get_net_keys(struct mesh_node *node)$/;"  f
+node_get_num_elements  node.c  /^uint8_t node_get_num_elements(struct mesh_node *node)$/;"     f
+node_get_primary       node.c  /^uint16_t node_get_primary(struct mesh_node *node)$/;" f
+node_get_primary_net_idx       node.c  /^uint16_t node_get_primary_net_idx(struct mesh_node *node)$/;" f
+node_get_prov  node.c  /^void *node_get_prov(struct mesh_node *node)$/;"       f
+node_get_sequence_number       node.c  /^uint32_t node_get_sequence_number(struct mesh_node *node)$/;" f
+node_is_provisioned    node.c  /^bool node_is_provisioned(struct mesh_node *node)$/;"  f
+node_local_data_handler        node.c  /^void node_local_data_handler(uint16_t src, uint32_t dst,$/;"  f
+node_local_model_register      node.c  /^bool node_local_model_register(uint8_t ele_idx, uint16_t model_id,$/;"        f
+node_local_vendor_model_register       node.c  /^bool node_local_vendor_model_register(uint8_t ele_idx, uint32_t model_id,$/;" f
+node_model_bind_callback       node.h  /^typedef int (*node_model_bind_callback)(uint16_t app_idx, int action);$/;"    t
+node_model_pub_callback        node.h  /^typedef void (*node_model_pub_callback)(struct mesh_publication *pub);$/;"    t
+node_model_pub_get     node.c  /^struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,$/;"  f
+node_model_pub_set     node.c  /^bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,$/;"   f
+node_model_recv_callback       node.h  /^typedef bool (*node_model_recv_callback)(uint16_t src, uint8_t *data,$/;"     t
+node_model_sub_callback        node.h  /^typedef void (*node_model_sub_callback)(uint16_t sub_addr, int action);$/;"   t
+node_net_key_add       node.c  /^bool node_net_key_add(struct mesh_node *node, uint16_t index)$/;"     f
+node_net_key_delete    node.c  /^bool node_net_key_delete(struct mesh_node *node, uint16_t index)$/;"  f
+node_new       node.c  /^struct mesh_node *node_new(void)$/;"  f
+node_parse_composition node.c  /^bool node_parse_composition(struct mesh_node *node, uint8_t *data, uint16_t len)$/;"  f
+node_set_composition   node.c  /^bool node_set_composition(struct mesh_node *node,$/;" f
+node_set_default_ttl   node.c  /^bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl)$/;"    f
+node_set_device_key    node.c  /^void node_set_device_key(struct mesh_node *node, uint8_t *key)$/;"    f
+node_set_element       node.c  /^bool node_set_element(struct mesh_node *node, uint8_t ele_idx)$/;"    f
+node_set_iv_index      node.c  /^bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index)$/;" f
+node_set_local_node    node.c  /^bool node_set_local_node(struct mesh_node *node)$/;"  f
+node_set_model node.c  /^bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id)$/;" f
+node_set_num_elements  node.c  /^void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)$/;"       f
+node_set_primary       node.c  /^void node_set_primary(struct mesh_node *node, uint16_t unicast)$/;"   f
+node_set_prov  node.c  /^void node_set_prov(struct mesh_node *node, void *prov)$/;"    f
+node_set_sequence_number       node.c  /^bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)$/;"       f
+nodes  node.c  /^static GList *nodes;$/;"      v       file:
+nonce  net.c   /^      uint8_t         *nonce;$/;"     m       struct:decrypt_params   file:
+notify_data    gatt.c  /^struct notify_data {$/;"      s       file:
+notify_io      gatt.c  /^static struct io *notify_io;$/;"      v       typeref:struct:io       file:
+notify_io_destroy      gatt.c  /^static void notify_io_destroy(void)$/;"       f       file:
+notify_mtu     gatt.c  /^static uint16_t notify_mtu;$/;"       v       file:
+notify_prov_out_cb     main.c  /^static void notify_prov_out_cb(DBusMessage *message, void *user_data)$/;"     f       file:
+notify_proxy_out_cb    main.c  /^static void notify_proxy_out_cb(DBusMessage *message, void *user_data)$/;"    f       file:
+notify_reply   gatt.c  /^static void notify_reply(DBusMessage *message, void *user_data)$/;"   f       file:
+num_ele        node.c  /^      uint8_t num_ele;$/;"    m       struct:mesh_node        file:
+num_ele        prov.c  /^      uint8_t  num_ele;$/;"   m       struct:__anon3  file:
+num_elements   net.c   /^      uint8_t num_elements;$/;"       m       struct:mesh_net file:
+onoff_app_idx  onoff-model.c   /^static uint16_t onoff_app_idx = APP_IDX_INVALID;$/;"  v       file:
+onoff_client_init      onoff-model.c   /^bool onoff_client_init(uint8_t ele)$/;"       f
+onoff_menu     onoff-model.c   /^static const struct bt_shell_menu onoff_menu = {$/;"  v       typeref:struct:bt_shell_menu    file:
+oob    node.c  /^      uint16_t oob;$/;"       m       struct:mesh_node        file:
+oob    node.h  /^      uint16_t oob;$/;"       m       struct:prov_svc_data
+oob_type_t     agent.h /^} oob_type_t;$/;"     t       typeref:enum:__anon2
+open_cb        net.c   /^      net_mesh_session_open_callback open_cb;$/;"     m       struct:mesh_net file:
+opt    main.c  /^static const struct bt_shell_opt opt = {$/;"  v       typeref:struct:bt_shell_opt     file:
+optargs        main.c  /^static const char **optargs[] = {$/;" v       file:
+options        main.c  /^static const struct option options[] = {$/;"  v       typeref:struct:option   file:
+out_msg        net.c   /^      uint8_t         *out_msg;$/;"   m       struct:decrypt_params   file:
+output_action  prov.c  /^      uint16_t output_action;$/;"     m       struct:__anon3  file:
+output_size    prov.c  /^      uint8_t  output_size;$/;"       m       struct:__anon3  file:
+packet net.c   /^      uint8_t                 *packet;$/;"    m       struct:decode_params    file:
+parms  config-client.c /^static uint32_t parms[8];$/;" v       file:
+parms  onoff-model.c   /^static uint32_t parms[8];$/;" v       file:
+parse_argument_on_off  main.c  /^static gboolean parse_argument_on_off(int argc, char *argv[],$/;"     f       file:
+parse_bindings prov-db.c       /^static bool parse_bindings(struct mesh_node *node, int ele_idx,$/;"   f       file:
+parse_composition_elements     prov-db.c       /^static bool parse_composition_elements(struct mesh_node *node,$/;"    f       file:
+parse_composition_models       prov-db.c       /^static bool parse_composition_models(struct mesh_node *node, int index,$/;"   f       file:
+parse_configuration_elements   prov-db.c       /^static bool parse_configuration_elements(struct mesh_node *node,$/;"  f       file:
+parse_configuration_models     prov-db.c       /^static bool parse_configuration_models(struct mesh_node *node, int ele_idx,$/;"       f       file:
+parse_mesh_service_data        main.c  /^static bool parse_mesh_service_data(const char *uuid, uint8_t *data, int len,$/;"     f       file:
+parse_model_pub        prov-db.c       /^static bool parse_model_pub(struct mesh_node *node, int ele_idx,$/;"  f       file:
+parse_node     prov-db.c       /^static bool parse_node(json_object *jnode, bool local)$/;"    f       file:
+parse_node_composition prov-db.c       /^static bool parse_node_composition(struct mesh_node *node, json_object *jcomp)$/;"    f       file:
+parse_node_keys        prov-db.c       /^static int parse_node_keys(struct mesh_node *node, json_object *jidxs,$/;"    f       file:
+parse_prov_service_data        main.c  /^static bool parse_prov_service_data(const char *uuid, uint8_t *data, int len,$/;"     f       file:
+parse_service_data     main.c  /^static bool parse_service_data(GDBusProxy *proxy, const char *target_uuid,$/;"        f       file:
+parse_unicast_range    prov-db.c       /^static bool parse_unicast_range(json_object *jobject)$/;"     f       file:
+pathloss       main.c  /^      dbus_int16_t pathloss;$/;"      m       struct:set_discovery_filter_args        file:
+pending_request        agent.c /^static struct input_request pending_request = {NONE, 0, NULL, NULL};$/;"      v       typeref:struct:input_request    file:
+period node.h  /^      uint8_t period;$/;"     m       struct:mesh_publication
+phase  net.c   /^      uint8_t                 phase;$/;"      m       struct:mesh_net_key     file:
+pid    node.h  /^      uint16_t pid;$/;"       m       struct:mesh_node_composition
+pipe_hup       gatt.c  /^static bool pipe_hup(struct io *io, void *user_data)$/;"      f       file:
+pipe_io_new    gatt.c  /^static struct io *pipe_io_new(int fd)$/;"     f       file:
+pipe_read      gatt.c  /^static bool pipe_read(struct io *io, bool prov, void *user_data)$/;"  f       file:
+pipe_read_prov gatt.c  /^static bool pipe_read_prov(struct io *io, void *user_data)$/;"        f       file:
+pipe_read_proxy        gatt.c  /^static bool pipe_read_proxy(struct io *io, void *user_data)$/;"       f       file:
+pipe_write     gatt.c  /^static bool pipe_write(struct io *io, void *user_data)$/;"    f       file:
+pkt_out        net.c   /^      GList *pkt_out; \/* Fully encoded packets awaiting Tx in order *\/$/;"  m       struct:mesh_net file:
+power_ten      agent.c /^static uint32_t power_ten(uint8_t power)$/;"  f       file:
+power_ten      prov.c  /^static uint32_t power_ten(uint8_t power)$/;"  f       file:
+primary        node.c  /^      uint16_t primary;$/;"   m       struct:mesh_node        file:
+primary_addr   net.c   /^      uint16_t primary_addr;$/;"      m       struct:mesh_net file:
+primary_net_idx        node.c  /^      uint16_t primary_net_idx;$/;"   m       struct:mesh_node        file:
+print_adapter  main.c  /^static void print_adapter(GDBusProxy *proxy, const char *description)$/;"     f       file:
+print_byte_array       util.c  /^void print_byte_array(const char *prefix, const void *ptr, int len)$/;"       f
+print_device   main.c  /^static void print_device(GDBusProxy *proxy, const char *description)$/;"      f       file:
+print_iter     main.c  /^static void print_iter(const char *label, const char *name,$/;"       f       file:
+print_model_pub        util.c  /^void print_model_pub(uint16_t ele_addr, uint32_t mod_id,$/;"  f
+print_property main.c  /^static void print_property(GDBusProxy *proxy, const char *name)$/;"   f       file:
+print_prov_service     main.c  /^static void print_prov_service(struct prov_svc_data *prov_data)$/;"   f       file:
+print_remaining_time   onoff-model.c   /^static void print_remaining_time(uint8_t remaining_time)$/;"  f       file:
+print_uuids    main.c  /^static void print_uuids(GDBusProxy *proxy)$/;"        f       file:
+privacy_key    net.c   /^      uint8_t privacy_key[16];$/;"    m       struct:net_key_parts    file:
+process_beacon net.c   /^static bool process_beacon(uint8_t *data, uint8_t size)$/;"   f       file:
+process_mesh_characteristic    main.c  /^static bool process_mesh_characteristic(GDBusProxy *proxy)$/;"        f       file:
+property_changed       main.c  /^static void property_changed(GDBusProxy *proxy, const char *name,$/;" f       file:
+prov   node.c  /^      void *prov;$/;" m       struct:mesh_node        file:
+prov_calc_ecdh prov.c  /^static void prov_calc_ecdh(DBusMessage *message, void *node)$/;"      f       file:
+prov_caps      prov.c  /^} __attribute__ ((packed))    prov_caps;$/;"  t       typeref:struct:__anon3  file:
+prov_complete  prov.c  /^bool prov_complete(struct mesh_node *node, uint8_t status)$/;"        f
+prov_data      prov.c  /^struct prov_data {$/;"        s       file:
+prov_data_ready        prov.c  /^bool prov_data_ready(struct mesh_node *node, uint8_t *buf, uint8_t len)$/;"   f
+prov_db_add_binding    prov-db.c       /^bool prov_db_add_binding(struct mesh_node *node, uint8_t ele_idx,$/;" f
+prov_db_add_new_node   prov-db.c       /^bool prov_db_add_new_node(struct mesh_node *node)$/;" f
+prov_db_add_node_composition   prov-db.c       /^bool prov_db_add_node_composition(struct mesh_node *node, uint8_t *data,$/;"  f
+prov_db_local_set_iv_index     prov-db.c       /^bool prov_db_local_set_iv_index(uint32_t iv_index, bool update, bool prov)$/;"        f
+prov_db_local_set_seq_num      prov-db.c       /^bool prov_db_local_set_seq_num(uint32_t seq_num)$/;"  f
+prov_db_node_keys      prov-db.c       /^bool prov_db_node_keys(struct mesh_node *node, GList *idxs, const char *desc)$/;"     f
+prov_db_node_set_iv_seq        prov-db.c       /^bool prov_db_node_set_iv_seq(struct mesh_node *node, uint32_t iv, uint32_t seq)$/;"   f
+prov_db_node_set_model_pub     prov-db.c       /^bool prov_db_node_set_model_pub(struct mesh_node *node, uint8_t ele_idx,$/;"  f
+prov_db_node_set_ttl   prov-db.c       /^bool prov_db_node_set_ttl(struct mesh_node *node, uint8_t ttl)$/;"    f
+prov_db_print_node_composition prov-db.c       /^void prov_db_print_node_composition(struct mesh_node *node)$/;"       f
+prov_db_read   prov-db.c       /^bool prov_db_read(const char *filename)$/;"   f
+prov_db_read_local_node        prov-db.c       /^bool prov_db_read_local_node(const char *filename, bool provisioner)$/;"      f
+prov_db_show   prov-db.c       /^bool prov_db_show(const char *filename)$/;"   f
+prov_disconn_reply     main.c  /^static void prov_disconn_reply(DBusMessage *message, void *user_data)$/;"     f       file:
+prov_done      prov.c  /^      provision_done_cb       prov_done;$/;"  m       struct:prov_data        file:
+prov_file_read prov-db.c       /^static char* prov_file_read(const char *filename)$/;" f       file:
+prov_file_write        prov-db.c       /^static void prov_file_write(json_object *jmain, bool local)$/;"       f       file:
+prov_filename  prov-db.c       /^static const char *prov_filename;$/;" v       file:
+prov_get_sec_level     prov.c  /^uint8_t prov_get_sec_level(void)$/;"  f
+prov_in        prov.c  /^      GDBusProxy              *prov_in;$/;"   m       struct:prov_data        file:
+prov_invite    prov.c  /^} __attribute__ ((packed))    prov_invite;$/;"        t       typeref:struct:__packed file:
+prov_net_key_index     main.c  /^static uint16_t prov_net_key_index = NET_IDX_PRIMARY;$/;"     v       file:
+prov_oob_pub_key       prov.c  /^static void prov_oob_pub_key(oob_type_t type, void *buf, uint16_t len,$/;"    f       file:
+prov_open      prov.c  /^bool prov_open(struct mesh_node *node, GDBusProxy *prov_in, uint16_t net_idx,$/;"     f
+prov_out_oob_done      prov.c  /^static void prov_out_oob_done(oob_type_t type, void *buf, uint16_t len,$/;"   f       file:
+prov_sec_level prov.c  /^static uint8_t prov_sec_level = MESH_PROV_SEC_MED;$/;"        v       file:
+prov_send_confirm      prov.c  /^static bool prov_send_confirm(void *node)$/;" f       file:
+prov_send_prov_data    prov.c  /^static bool prov_send_prov_data(void *node)$/;"       f       file:
+prov_send_pub_key      prov.c  /^static void prov_send_pub_key(struct mesh_node *node)$/;"     f       file:
+prov_set_sec_level     prov.c  /^bool prov_set_sec_level(uint8_t level)$/;"    f
+prov_start     prov.c  /^} __attribute__ ((packed))    prov_start;$/;" t       typeref:struct:__anon4  file:
+prov_start_cmplt       prov.c  /^static void prov_start_cmplt(DBusMessage *message, void *node)$/;"    f       file:
+prov_svc_data  node.h  /^struct prov_svc_data {$/;"    s
+provision_done_cb      prov.h  /^typedef void (*provision_done_cb)(void *user_data, int status);$/;"   t
+provisioner    net.c   /^      bool provisioner;$/;"   m       struct:mesh_net file:
+provisioner    node.c  /^      bool provisioner;$/;"   m       struct:mesh_node        file:
+proxy  gatt.c  /^      GDBusProxy *proxy;$/;"  m       struct:notify_data      file:
+proxy  gatt.c  /^      GDBusProxy *proxy;$/;"  m       struct:write_data       file:
+proxy  main.c  /^      GDBusProxy *proxy;$/;"  m       struct:mesh_device      file:
+proxy  main.c  /^GDBusProxy *proxy;$/;"        m       struct:adapter  file:
+proxy  net.c   /^      bool                    proxy;$/;"      m       struct:decode_params    file:
+proxy  net.c   /^      bool            proxy;$/;"      m       struct:mesh_sar_msg     file:
+proxy  node.h  /^      bool proxy;$/;" m       struct:mesh_node_composition
+proxy_added    main.c  /^static void proxy_added(GDBusProxy *proxy, void *user_data)$/;"       f       file:
+proxy_ctl_rxed net.c   /^static bool proxy_ctl_rxed(uint16_t net_idx, uint32_t iv_index,$/;"   f       file:
+proxy_in       net.c   /^      GDBusProxy *proxy_in;$/;"       m       struct:mesh_net file:
+proxy_leak     main.c  /^static void proxy_leak(gpointer data)$/;"     f       file:
+proxy_removed  main.c  /^static void proxy_removed(GDBusProxy *proxy, void *user_data)$/;"     f       file:
+prv_pub_key    prov.c  /^      uint8_t prv_pub_key[64];$/;"    m       struct:__anon5  file:
+pub    node.c  /^      struct mesh_publication *pub;$/;"       m       struct:mesh_model       typeref:struct:mesh_model::mesh_publication     file:
+pub    node.h  /^      node_model_pub_callback pub;$/;"        m       struct:mesh_model_ops
+pub_key        prov.c  /^      uint8_t pub_key;$/;"    m       struct:__anon4  file:
+pub_type       prov.c  /^      uint8_t  pub_type;$/;"  m       struct:__anon3  file:
+put_uint16     prov-db.c       /^static void put_uint16(json_object *jobject, const char *desc, uint16_t value)$/;"    f       file:
+put_uint16_array_entry prov-db.c       /^static void put_uint16_array_entry(json_object *jarray, uint16_t value)$/;"   f       file:
+put_uint16_list        prov-db.c       /^static void put_uint16_list(json_object *jarray, GList *list)$/;"     f       file:
+put_uint32     prov-db.c       /^static void put_uint32(json_object *jobject, const char *desc, uint32_t value)$/;"    f       file:
+put_uint32_array_entry prov-db.c       /^static void put_uint32_array_entry(json_object *jarray, uint32_t value)$/;"   f       file:
+rand_auth      prov.c  /^      uint8_t                 rand_auth[32];$/;"      m       struct:prov_data        file:
+range_cmp      net.c   /^static int range_cmp(const void *a, const void *b)$/;"        f       file:
+read_input_parameters  config-client.c /^static uint32_t read_input_parameters(int argc, char *argv[])$/;"     f       file:
+read_input_parameters  onoff-model.c   /^static uint32_t read_input_parameters(int argc, char *argv[])$/;"     f       file:
+read_json_db   prov-db.c       /^static bool read_json_db(const char *filename, bool provisioner, bool local)$/;"      f       file:
+recv   node.h  /^      node_model_recv_callback recv;$/;"      m       struct:mesh_model_ops
+relay  node.h  /^      bool relay;$/;" m       struct:mesh_node_composition
+request_ascii  agent.c /^static bool request_ascii(uint16_t len)$/;"   f       file:
+request_decimal        agent.c /^static bool request_decimal(uint16_t len)$/;" f       file:
+request_hexadecimal    agent.c /^static bool request_hexadecimal(uint16_t len)$/;"     f       file:
+resend_segs    net.c   /^static void resend_segs(struct mesh_sar_msg *sar)$/;" f       file:
+resend_unacked_segs    net.c   /^static void resend_unacked_segs(gpointer data, gpointer user_data)$/;"        f       file:
+reset_input_request    agent.c /^static void reset_input_request(void)$/;"     f       file:
+response_ascii agent.c /^static void response_ascii(const char *input, void *user_data)$/;"    f       file:
+response_decimal       agent.c /^static void response_decimal(const char *input, void *user_data)$/;"  f       file:
+response_hexadecimal   agent.c /^static void response_hexadecimal(const char *input, void *user_data)$/;"      f       file:
+response_output        agent.c /^static void response_output(const char *input, void *user_data)$/;"   f       file:
+restore_model_state    node.c  /^static gboolean restore_model_state(gpointer data)$/;"        f       file:
+retransmit     node.h  /^      uint8_t retransmit;$/;" m       struct:mesh_publication
+rssi   main.c  /^      dbus_uint16_t rssi;$/;" m       struct:set_discovery_filter_args        file:
+salt   prov.c  /^      uint8_t                 salt[16];$/;"   m       struct:prov_data        file:
+sar_in net.c   /^      GList *sar_in;  \/* Incoming segmented messages in progress *\/$/;"     m       struct:mesh_net file:
+sar_in_ack_timeout     net.c   /^static gboolean sar_in_ack_timeout(void *user_data)$/;"       f       file:
+sar_in_msg_timeout     net.c   /^static gboolean sar_in_msg_timeout(void *user_data)$/;"       f       file:
+sar_out_ack_timeout    net.c   /^static gboolean sar_out_ack_timeout(void *user_data)$/;"      f       file:
+sar_out_msg_timeout    net.c   /^static gboolean sar_out_msg_timeout(void *user_data)$/;"      f       file:
+security2str   main.c  /^static const char *security2str(uint8_t level)$/;"    f       file:
+segN   net.c   /^      uint8_t         segN;$/;"       m       struct:mesh_sar_msg     file:
+seg_rxed       net.c   /^static bool seg_rxed(uint16_t net_idx, uint32_t iv_index, bool ctl,$/;"       f       file:
+segmented      net.c   /^      bool            segmented;$/;"  m       struct:mesh_sar_msg     file:
+send_cmd       onoff-model.c   /^static bool send_cmd(uint8_t *buf, uint16_t len)$/;"  f       file:
+send_mesh_pkt  net.c   /^static void send_mesh_pkt(struct mesh_pkt *pkt)$/;"   f       file:
+send_pkt_cmplt net.c   /^static void send_pkt_cmplt(DBusMessage *message, void *user_data)$/;" f       file:
+send_sar_ack   net.c   /^static void send_sar_ack(struct mesh_sar_msg *sar)$/;"        f       file:
+send_seg       net.c   /^static void send_seg(struct mesh_sar_msg *sar, uint8_t seg)$/;"       f       file:
+seqAuth        net.c   /^      uint32_t        seqAuth;$/;"    m       struct:mesh_sar_msg     file:
+seq_num        net.c   /^      uint32_t        seq_num;$/;"    m       struct:decrypt_params   file:
+seq_num        net.c   /^      uint32_t seq_num;$/;"   m       struct:mesh_net file:
+seq_num_reserved       net.c   /^      uint32_t seq_num_reserved;$/;"  m       struct:mesh_net file:
+seq_number     node.c  /^      uint32_t seq_number;$/;"        m       struct:mesh_node        file:
+server_cbs     config-server.c /^static struct mesh_model_ops server_cbs = {$/;"       v       typeref:struct:mesh_model_ops   file:
+server_msg_recvd       config-server.c /^static bool server_msg_recvd(uint16_t src, uint8_t *data,$/;" f       file:
+service        main.c  /^      GDBusProxy *service;$/;"        m       struct:__anon1  file:
+service_is_mesh        main.c  /^static bool service_is_mesh(GDBusProxy *proxy, const char *target_uuid)$/;"   f       file:
+service_list   main.c  /^GList *service_list;$/;"      v
+session_open   main.c  /^      bool session_open;$/;"  m       struct:__anon1  file:
+session_open_cb        main.c  /^static void session_open_cb (int status)$/;"  f       file:
+set_connected_device   main.c  /^static void set_connected_device(GDBusProxy *proxy)$/;"       f       file:
+set_discovery_filter_args      main.c  /^struct set_discovery_filter_args {$/;"        s       file:
+set_discovery_filter_reply     main.c  /^static void set_discovery_filter_reply(DBusMessage *message, void *user_data)$/;"     f       file:
+set_discovery_filter_setup     main.c  /^static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)$/;"    f       file:
+set_local_iv_index     prov-db.c       /^static void set_local_iv_index(json_object *jobj, uint32_t idx, bool update)$/;"      f       file:
+set_menu_prompt        util.c  /^void set_menu_prompt(const char *name, const char *id)$/;"    f
+set_scan_filter_commit main.c  /^static void set_scan_filter_commit(void)$/;"  f       file:
+set_scan_filter_uuids  main.c  /^static void set_scan_filter_uuids(char *filters[])$/;"        f       file:
+set_sequence_number    net.c   /^void set_sequence_number(uint32_t seq_num)$/;"        f
+setup_whitelist        net.c   /^static void setup_whitelist()$/;"     f       file:
+size   net.c   /^      uint8_t                 size;$/;"       m       struct:decode_params    file:
+src    net.c   /^      uint16_t        src;$/;"        m       struct:decrypt_params   file:
+src    net.c   /^      uint16_t        src;$/;"        m       struct:mesh_sar_msg     file:
+start  prov.c  /^      prov_start      start;$/;"      m       struct:__anon5  file:
+start_discovery_reply  main.c  /^static void start_discovery_reply(DBusMessage *message, void *user_data)$/;"  f       file:
+state  prov.c  /^      uint8_t                 state;$/;"      m       struct:prov_data        file:
+static_type    prov.c  /^      uint8_t  static_type;$/;"       m       struct:__anon3  file:
+str2hex        util.c  /^bool str2hex(const char *str, uint16_t in_len, uint8_t *out,$/;"      f
+sub    node.h  /^      node_model_sub_callback sub;$/;"        m       struct:mesh_model_ops
+subscriptions  node.c  /^      GList *subscriptions;$/;"       m       struct:mesh_model       file:
+swap_u256_bytes        util.c  /^void swap_u256_bytes(uint8_t *u256)$/;"       f
+szmic  net.c   /^      bool            szmic;$/;"      m       struct:decrypt_params   file:
+szmic  net.c   /^      bool            szmic;$/;"      m       struct:mesh_sar_msg     file:
+target config-client.c /^static uint32_t target;$/;"   v       file:
+target onoff-model.c   /^static uint32_t target;$/;"   v       file:
+trans  net.c   /^      uint8_t         *trans;$/;"     m       struct:decrypt_params   file:
+trans_id       onoff-model.c   /^static uint8_t trans_id;$/;"  v       file:
+transport      main.c  /^      char *transport;$/;"    m       struct:set_discovery_filter_args        file:
+try_decode     net.c   /^static void try_decode(gpointer data, gpointer user_data)$/;" f       file:
+try_decrypt    net.c   /^static void try_decrypt(gpointer data, gpointer user_data)$/;"        f       file:
+try_virt_decrypt       net.c   /^static void try_virt_decrypt(gpointer data, gpointer user_data)$/;"   f       file:
+ttl    net.c   /^      uint8_t         ttl;$/;"        m       struct:mesh_sar_msg     file:
+ttl    node.c  /^      uint8_t ttl;$/;"        m       struct:mesh_node        file:
+ttl    node.h  /^      uint8_t ttl;$/;"        m       struct:mesh_publication
+type   agent.c /^      oob_type_t type;$/;"    m       struct:input_request    file:
+type   main.c  /^      uint8_t type;$/;"       m       struct:__anon1  file:
+u      node.h  /^      } u;$/;"        m       struct:mesh_publication typeref:union:mesh_publication::__anon6
+u16_highest_bit        prov.c  /^static uint8_t u16_highest_bit(uint16_t mask)$/;"     f       file:
+unicast        main.c  /^      uint16_t unicast;$/;"   m       struct:__anon1  file:
+update_device_info     main.c  /^static void update_device_info(GDBusProxy *proxy)$/;" f       file:
+user_data      agent.c /^      void *user_data;$/;"    m       struct:input_request    file:
+user_data      gatt.c  /^      void *user_data;$/;"    m       struct:notify_data      file:
+user_data      gatt.c  /^      void *user_data;$/;"    m       struct:write_data       file:
+user_data      node.c  /^      void *user_data;$/;"    m       struct:mesh_model       file:
+user_data      prov.c  /^      void                    *user_data;$/;" m       struct:prov_data        file:
+uuids  main.c  /^      char **uuids;$/;"       m       struct:set_discovery_filter_args        file:
+uuids_len      main.c  /^      size_t uuids_len;$/;"   m       struct:set_discovery_filter_args        file:
+va128  net.c   /^      uint8_t         va128[16];$/;"  m       struct:mesh_virt_addr   file:
+va16   net.c   /^      uint16_t        va16;$/;"       m       struct:mesh_virt_addr   file:
+va32   net.c   /^      uint32_t        va32;$/;"       m       struct:mesh_virt_addr   file:
+va_128 node.h  /^              uint8_t va_128[16];$/;" m       union:mesh_publication::__anon6
+verify_config_target   config-client.c /^static bool verify_config_target(uint32_t dst)$/;"    f       file:
+vid    node.h  /^      uint16_t vid;$/;"       m       struct:mesh_node_composition
+virt_addrs     net.c   /^static GList *virt_addrs = NULL;$/;"  v       file:
+virtual_rxed   net.c   /^static bool virtual_rxed(uint8_t *nonce, uint16_t net_idx,$/;"        f       file:
+whitefilter_add        net.c   /^static void whitefilter_add(gpointer data, gpointer user_data)$/;"    f       file:
+write_data     gatt.c  /^struct write_data {$/;"       s       file:
+write_data_free        gatt.c  /^static void write_data_free(void *user_data)$/;"      f       file:
+write_io       gatt.c  /^static struct io *write_io;$/;"       v       typeref:struct:io       file:
+write_io_destroy       gatt.c  /^static void write_io_destroy(void)$/;"        f       file:
+write_mtu      gatt.c  /^static uint16_t write_mtu;$/;"        v       file:
diff --git a/mesh/util.c b/mesh/util.c
new file mode 100644 (file)
index 0000000..d38d875
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <glib.h>
+
+#include "src/shared/shell.h"
+#include "src/shared/util.h"
+#include "mesh/mesh-net.h"
+#include "mesh/node.h"
+#include "mesh/util.h"
+
+void set_menu_prompt(const char *name, const char *id)
+{
+       char *prompt;
+
+       prompt = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", name,
+                                       id ? ": Target = " : "", id ? id : "");
+       bt_shell_set_prompt(prompt);
+       g_free(prompt);
+}
+
+void print_byte_array(const char *prefix, const void *ptr, int len)
+{
+       const uint8_t *data = ptr;
+       char *line, *bytes;
+       int i;
+
+       line = g_malloc(strlen(prefix) + (16 * 3) + 2);
+       sprintf(line, "%s ", prefix);
+       bytes = line + strlen(prefix) + 1;
+
+       for (i = 0; i < len; ++i) {
+               sprintf(bytes, "%2.2x ", data[i]);
+               if ((i + 1) % 16) {
+                       bytes += 3;
+               } else {
+                       bt_shell_printf("\r%s\n", line);
+                       bytes = line + strlen(prefix) + 1;
+               }
+       }
+
+       if (i % 16)
+               bt_shell_printf("\r%s\n", line);
+
+       g_free(line);
+}
+
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
+               uint16_t out_len)
+{
+       uint16_t i;
+
+       if (in_len < out_len * 2)
+               return false;
+
+       for (i = 0; i < out_len; i++) {
+               if (sscanf(&str[i * 2], "%02hhx", &out[i]) != 1)
+                       return false;
+       }
+
+       return true;
+}
+
+size_t hex2str(uint8_t *in, size_t in_len, char *out,
+               size_t out_len)
+{
+       static const char hexdigits[] = "0123456789abcdef";
+       size_t i;
+
+       if(in_len * 2 > out_len - 1)
+               return 0;
+
+       for (i = 0; i < in_len; i++) {
+               out[i * 2] = hexdigits[in[i] >> 4];
+               out[i * 2 + 1] = hexdigits[in[i] & 0xf];
+       }
+
+       out[in_len * 2] = '\0';
+       return i;
+}
+
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf)
+{
+       if (opcode <= 0x7e) {
+               buf[0] = opcode;
+               return 1;
+       } else if (opcode >= 0x8000 && opcode <= 0xbfff) {
+               put_be16(opcode, buf);
+               return 2;
+       } else if (opcode >= 0xc00000 && opcode <= 0xffffff) {
+               buf[0] = (opcode >> 16) & 0xff;
+               put_be16(opcode, buf + 1);
+               return 3;
+       } else {
+               bt_shell_printf("Illegal Opcode %x", opcode);
+               return 0;
+       }
+}
+
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n)
+{
+       if (!n || !opcode || sz < 1) return false;
+
+       switch (buf[0] & 0xc0) {
+       case 0x00:
+       case 0x40:
+               /* RFU */
+               if (buf[0] == 0x7f)
+                       return false;
+
+               *n = 1;
+               *opcode = buf[0];
+               break;
+
+       case 0x80:
+               if (sz < 2)
+                       return false;
+
+               *n = 2;
+               *opcode = get_be16(buf);
+               break;
+
+       case 0xc0:
+               if (sz < 3)
+                       return false;
+
+               *n = 3;
+               *opcode = get_be16(buf + 1);
+               *opcode |= buf[0] << 16;
+               break;
+
+       default:
+               bt_shell_printf("Bad Packet:\n");
+               print_byte_array("\t", (void *) buf, sz);
+               return false;
+       }
+
+       return true;
+}
+
+const char *mesh_status_str(uint8_t status)
+{
+       switch (status) {
+       case MESH_STATUS_SUCCESS: return "Success";
+       case MESH_STATUS_INVALID_ADDRESS: return "Invalid Address";
+       case MESH_STATUS_INVALID_MODEL: return "Invalid Model";
+       case MESH_STATUS_INVALID_APPKEY: return "Invalid AppKey";
+       case MESH_STATUS_INVALID_NETKEY: return "Invalid NetKey";
+       case MESH_STATUS_INSUFF_RESOURCES: return "Insufficient Resources";
+       case MESH_STATUS_IDX_ALREADY_STORED: return "Key Idx Already Stored";
+       case MESH_STATUS_INVALID_PUB_PARAM: return "Invalid Publish Parameters";
+       case MESH_STATUS_NOT_SUB_MOD: return "Not a Subscribe Model";
+       case MESH_STATUS_STORAGE_FAIL: return "Storage Failure";
+       case MESH_STATUS_FEAT_NOT_SUP: return "Feature Not Supported";
+       case MESH_STATUS_CANNOT_UPDATE: return "Cannot Update";
+       case MESH_STATUS_CANNOT_REMOVE: return "Cannot Remove";
+       case MESH_STATUS_CANNOT_BIND: return "Cannot bind";
+       case MESH_STATUS_UNABLE_CHANGE_STATE: return "Unable to change state";
+       case MESH_STATUS_CANNOT_SET: return "Cannot set";
+       case MESH_STATUS_UNSPECIFIED_ERROR: return "Unspecified error";
+       case MESH_STATUS_INVALID_BINDING: return "Invalid Binding";
+
+       default: return "Unknown";
+       }
+}
+
+void print_model_pub(uint16_t ele_addr, uint32_t mod_id,
+                                               struct mesh_publication *pub)
+{
+       bt_shell_printf("\tElement: %4.4x\n", ele_addr);
+       bt_shell_printf("\tPub Addr: %4.4x", pub->u.addr16);
+       if (mod_id > 0xffff0000)
+               bt_shell_printf("\tModel: %8.8x \n", mod_id);
+       else
+               bt_shell_printf("\tModel: %4.4x \n",
+                               (uint16_t) (mod_id & 0xffff));
+       bt_shell_printf("\tApp Key Idx: %4.4x", pub->app_idx);
+       bt_shell_printf("\tTTL: %2.2x", pub->ttl);
+}
+
+void swap_u256_bytes(uint8_t *u256)
+{
+       int i;
+
+       /* End-to-End byte reflection of 32 octet buffer */
+       for (i = 0; i < 16; i++) {
+               u256[i] ^= u256[31 - i];
+               u256[31 - i] ^= u256[i];
+               u256[i] ^= u256[31 - i];
+       }
+}
diff --git a/mesh/util.h b/mesh/util.h
new file mode 100644 (file)
index 0000000..c3facfa
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 <stdbool.h>
+
+struct mesh_publication;
+
+#define OP_UNRELIABLE                  0x0100
+
+void set_menu_prompt(const char *name, const char *id);
+void print_byte_array(const char *prefix, const void *ptr, int len);
+bool str2hex(const char *str, uint16_t in_len, uint8_t *out_buf,
+               uint16_t out_len);
+size_t hex2str(uint8_t *in, size_t in_len, char *out,
+               size_t out_len);
+uint16_t mesh_opcode_set(uint32_t opcode, uint8_t *buf);
+bool mesh_opcode_get(const uint8_t *buf, uint16_t sz, uint32_t *opcode, int *n);
+const char *mesh_status_str(uint8_t status);
+void print_model_pub(uint16_t ele_addr, uint32_t mod_id,
+                                               struct mesh_publication *pub);
+void swap_u256_bytes(uint8_t *u256);
index 81e9166..a9544e1 100755 (executable)
@@ -33,6 +33,7 @@
 #include <inttypes.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 
 #include "src/shared/util.h"
 #include "bt.h"
@@ -1965,7 +1966,7 @@ static bool avrcp_folder_item(struct avctp_frame *avctp_frame,
        print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
                                        namelen, namelen);
 
-       print_field("%*cName: ", indent, ' ');
+       printf("%*cName: ", indent+8, ' ');
        for (; namelen > 0; namelen--) {
                uint8_t c;
                if (!l2cap_frame_get_u8(frame, &c))
@@ -1973,6 +1974,7 @@ static bool avrcp_folder_item(struct avctp_frame *avctp_frame,
 
                printf("%1c", isprint(c) ? c : '.');
        }
+       printf("\n");
 
        return true;
 }
@@ -1984,8 +1986,7 @@ static bool avrcp_attribute_entry_list(struct avctp_frame *avctp_frame,
 
        for (; count > 0; count--) {
                uint32_t attr;
-               uint16_t charset;
-               uint8_t len;
+               uint16_t charset, len;
 
                if (!l2cap_frame_get_be32(frame, &attr))
                        return false;
@@ -1999,13 +2000,13 @@ static bool avrcp_attribute_entry_list(struct avctp_frame *avctp_frame,
                print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ',
                                                charset, charset2str(charset));
 
-               if (!l2cap_frame_get_u8(frame, &len))
+               if (!l2cap_frame_get_be16(frame, &len))
                        return false;
 
-               print_field("%*cAttributeLength: 0x%02x (%u)", indent, ' ',
+               print_field("%*cAttributeLength: 0x%04x (%u)", indent, ' ',
                                                len, len);
 
-               print_field("%*cAttributeValue: ", indent, ' ');
+               printf("%*cAttributeValue: ", indent+8, ' ');
                for (; len > 0; len--) {
                        uint8_t c;
 
@@ -2014,6 +2015,7 @@ static bool avrcp_attribute_entry_list(struct avctp_frame *avctp_frame,
 
                        printf("%1c", isprint(c) ? c : '.');
                }
+               printf("\n");
        }
 
        return true;
@@ -2051,7 +2053,7 @@ static bool avrcp_media_element_item(struct avctp_frame *avctp_frame,
        print_field("%*cNameLength: 0x%04x (%u)", indent, ' ',
                                        namelen, namelen);
 
-       print_field("%*cName: ", indent, ' ');
+       printf("%*cName: ", indent+8, ' ');
        for (; namelen > 0; namelen--) {
                uint8_t c;
                if (!l2cap_frame_get_u8(frame, &c))
@@ -2059,6 +2061,7 @@ static bool avrcp_media_element_item(struct avctp_frame *avctp_frame,
 
                printf("%1c", isprint(c) ? c : '.');
        }
+       printf("\n");
 
        if (!l2cap_frame_get_u8(frame, &count))
                return false;
index 3524faa..f5ef4c0 100755 (executable)
@@ -368,7 +368,7 @@ static bool avdtp_discover(struct avdtp_frame *avdtp_frame)
                        print_field("%*cMedia Type: %s (0x%02x)", 2, ' ',
                                        mediatype2str(info >> 4), info >> 4);
                        print_field("%*cSEP Type: %s (0x%02x)", 2, ' ',
-                                               info & 0x04 ? "SNK" : "SRC",
+                                               info & 0x08 ? "SNK" : "SRC",
                                                (info >> 3) & 0x01);
                        print_field("%*cIn use: %s", 2, ' ',
                                                seid & 0x02 ? "Yes" : "No");
@@ -776,7 +776,8 @@ void avdtp_packet(const struct l2cap_frame *frame)
                ret = avdtp_signalling_packet(&avdtp_frame);
                break;
        default:
-               packet_hexdump(frame->data, frame->size);
+               if (packet_has_filter(PACKET_FILTER_SHOW_A2DP_STREAM))
+                       packet_hexdump(frame->data, frame->size);
                return;
        }
 
index 01392e8..62c4f5a 100755 (executable)
@@ -33,6 +33,7 @@
 #include <inttypes.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 
 #include "src/shared/util.h"
 #include "bt.h"
@@ -157,9 +158,9 @@ static bool setup_conn_req(struct bnep_frame *bnep_frame, uint8_t indent)
        }
 
        print_field("%*cDst: 0x%x(%s)", indent, ' ', dst_uuid,
-                                               uuid32_to_str(dst_uuid));
+                                               bt_uuid32_to_str(dst_uuid));
        print_field("%*cSrc: 0x%x(%s)", indent, ' ', src_uuid,
-                                               uuid32_to_str(src_uuid));
+                                               bt_uuid32_to_str(src_uuid));
        return true;
 }
 
old mode 100755 (executable)
new mode 100644 (file)
index 80edbeb..b001608
@@ -27,6 +27,7 @@
 #endif
 
 #include <stdio.h>
+#include <inttypes.h>
 
 #include "src/shared/util.h"
 #include "display.h"
@@ -35,6 +36,9 @@
 #include "ll.h"
 #include "vendor.h"
 #include "broadcom.h"
+
+#define COLOR_UNKNOWN_FEATURE_BIT      COLOR_WHITE_BG
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 #include "uuid.h"
 #endif
@@ -44,6 +48,184 @@ static void print_status(uint8_t status)
        packet_print_error("Status", status);
 }
 
+static void print_sco_routing(uint8_t routing)
+{
+       const char *str;
+
+       switch (routing) {
+       case 0x00:
+               str = "PCM";
+               break;
+       case 0x01:
+               str = "Transport";
+               break;
+       case 0x02:
+               str = "Codec";
+               break;
+       case 0x03:
+               str = "I2S";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("SCO routing: %s (0x%2.2x)", str, routing);
+}
+
+static void print_pcm_interface_rate(uint8_t rate)
+{
+       const char *str;
+
+       switch (rate) {
+       case 0x00:
+               str = "128 KBps";
+               break;
+       case 0x01:
+               str = "256 KBps";
+               break;
+       case 0x02:
+               str = "512 KBps";
+               break;
+       case 0x03:
+               str = "1024 KBps";
+               break;
+       case 0x04:
+               str = "2048 KBps";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("PCM interface rate: %s (0x%2.2x)", str, rate);
+}
+
+static void print_frame_type(uint8_t type)
+{
+       const char *str;
+
+       switch (type) {
+       case 0x00:
+               str = "Short";
+               break;
+       case 0x01:
+               str = "Long";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Frame type: %s (0x%2.2x)", str, type);
+}
+
+static void print_sync_mode(uint8_t mode)
+{
+       const char *str;
+
+       switch (mode) {
+       case 0x00:
+               str = "Slave";
+               break;
+       case 0x01:
+               str = "Master";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Sync mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_clock_mode(uint8_t mode)
+{
+       const char *str;
+
+       switch (mode) {
+       case 0x00:
+               str = "Slave";
+               break;
+       case 0x01:
+               str = "Master";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Clock mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_sleep_mode(uint8_t mode)
+{
+       const char *str;
+
+       switch (mode) {
+       case 0x00:
+               str = "No sleep mode";
+               break;
+       case 0x01:
+               str = "UART";
+               break;
+       case 0x02:
+               str = "UART with messaging";
+               break;
+       case 0x03:
+               str = "USB";
+               break;
+       case 0x04:
+               str = "H4IBSS";
+               break;
+       case 0x05:
+               str = "USB with Host wake";
+               break;
+       case 0x06:
+               str = "SDIO";
+               break;
+       case 0x07:
+               str = "UART CS-N";
+               break;
+       case 0x08:
+               str = "SPI";
+               break;
+       case 0x09:
+               str = "H5";
+               break;
+       case 0x0a:
+               str = "H4DS";
+               break;
+       case 0x0c:
+               str = "UART with BREAK";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Sleep mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_clock_setting(uint8_t clock)
+{
+       const char *str;
+
+       switch (clock) {
+       case 0x01:
+               str = "48 Mhz";
+               break;
+       case 0x02:
+               str = "24 Mhz";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("UART clock: %s (0x%2.2x)", str, clock);
+}
+
 static void null_cmd(const void *data, uint8_t size)
 {
 }
@@ -60,6 +242,91 @@ static void write_bd_addr_cmd(const void *data, uint8_t size)
        packet_print_addr("Address", data, false);
 }
 
+static void update_uart_baud_rate_cmd(const void *data, uint8_t size)
+{
+       uint16_t enc_rate = get_le16(data);
+       uint32_t exp_rate = get_le32(data + 2);
+
+       if (enc_rate == 0x0000)
+               print_field("Encoded baud rate: Not used (0x0000)");
+       else
+               print_field("Encoded baud rate: 0x%4.4x", enc_rate);
+
+       print_field("Explicit baud rate: %u Mbps", exp_rate);
+}
+
+static void write_sco_pcm_int_param_cmd(const void *data, uint8_t size)
+{
+       uint8_t routing = get_u8(data);
+       uint8_t rate = get_u8(data + 1);
+       uint8_t frame_type = get_u8(data + 2);
+       uint8_t sync_mode = get_u8(data + 3);
+       uint8_t clock_mode = get_u8(data + 4);
+
+       print_sco_routing(routing);
+       print_pcm_interface_rate(rate);
+       print_frame_type(frame_type);
+       print_sync_mode(sync_mode);
+       print_clock_mode(clock_mode);
+}
+
+static void read_sco_pcm_int_param_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t routing = get_u8(data + 1);
+       uint8_t rate = get_u8(data + 2);
+       uint8_t frame_type = get_u8(data + 3);
+       uint8_t sync_mode = get_u8(data + 4);
+       uint8_t clock_mode = get_u8(data + 5);
+
+       print_status(status);
+       print_sco_routing(routing);
+       print_pcm_interface_rate(rate);
+       print_frame_type(frame_type);
+       print_sync_mode(sync_mode);
+       print_clock_mode(clock_mode);
+}
+
+static void set_sleepmode_param_cmd(const void *data, uint8_t size)
+{
+       uint8_t mode = get_u8(data);
+
+       print_sleep_mode(mode);
+
+       packet_hexdump(data + 1, size - 1);
+}
+
+static void read_sleepmode_param_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t mode = get_u8(data + 1);
+
+       print_status(status);
+       print_sleep_mode(mode);
+
+       packet_hexdump(data + 2, size - 2);
+}
+
+static void enable_radio_cmd(const void *data, uint8_t size)
+{
+       uint8_t mode = get_u8(data);
+       const char *str;
+
+       switch (mode) {
+       case 0x00:
+               str = "Disable the radio";
+               break;
+       case 0x01:
+               str = "Enable the radio";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
 static void enable_usb_hid_emulation_cmd(const void *data, uint8_t size)
 {
        uint8_t enable = get_u8(data);
@@ -80,6 +347,22 @@ static void enable_usb_hid_emulation_cmd(const void *data, uint8_t size)
        print_field("Enable: %s (0x%2.2x)", str, enable);
 }
 
+static void read_uart_clock_setting_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+       uint8_t clock = get_u8(data + 1);
+
+       print_status(status);
+       print_clock_setting(clock);
+}
+
+static void write_uart_clock_setting_cmd(const void *data, uint8_t size)
+{
+       uint8_t clock = get_u8(data);
+
+       print_clock_setting(clock);
+}
+
 static void write_ram_cmd(const void *data, uint8_t size)
 {
        uint32_t addr = get_le32(data);
@@ -89,6 +372,24 @@ static void write_ram_cmd(const void *data, uint8_t size)
        packet_hexdump(data + 4, size - 4);
 }
 
+static void read_ram_cmd(const void *data, uint8_t size)
+{
+       uint32_t addr = get_le32(data);
+       uint8_t length = get_u8(data + 4);
+
+       print_field("Address: 0x%8.8x", addr);
+       print_field("Length: %u", length);
+}
+
+static void read_ram_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+
+       print_status(status);
+
+       packet_hexdump(data + 1, size - 1);
+}
+
 static void launch_ram_cmd(const void *data, uint8_t size)
 {
        uint32_t addr = get_le32(data);
@@ -699,6 +1000,52 @@ static void read_vid_pid_rsp(const void *data, uint8_t size)
        print_field("Product: %4.4x:%4.4x", vid, pid);
 }
 
+static const struct {
+       uint8_t bit;
+       const char *str;
+} features_table[] = {
+       {  0, "Multi-AV transport bandwidth reducer"    },
+       {  1, "WBS SBC"                                 },
+       {  2, "FW LC-PLC"                               },
+       {  3, "FM SBC internal stack"                   },
+       { }
+};
+
+static void print_features(const uint8_t *features_array)
+{
+       uint64_t mask, features = 0;
+       char str[41];
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               sprintf(str + (i * 5), " 0x%2.2x", features_array[i]);
+               features |= ((uint64_t) features_array[i]) << (i * 8);
+       }
+
+       print_field("Features:%s", str);
+
+       mask = features;
+
+       for (i = 0; features_table[i].str; i++) {
+               if (features & (((uint64_t) 1) << features_table[i].bit)) {
+                       print_field("  %s", features_table[i].str);
+                       mask &= ~(((uint64_t) 1) << features_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_FEATURE_BIT, "  Unknown features "
+                                               "(0x%16.16" PRIx64 ")", mask);
+}
+
+static void read_controller_features_rsp(const void *data, uint8_t size)
+{
+       uint8_t status = get_u8(data);
+
+       print_status(status);
+       print_features(data + 1);
+}
+
 static void read_verbose_version_info_rsp(const void *data, uint8_t size)
 {
        uint8_t status = get_u8(data);
@@ -823,24 +1170,53 @@ static const struct vendor_ocf vendor_ocf_table[] = {
        { 0x001, "Write BD ADDR",
                        write_bd_addr_cmd, 6, true,
                        status_rsp, 1, true },
-       { 0x018, "Update UART Baud Rate" },
-       { 0x027, "Set Sleepmode Param" },
+       { 0x018, "Update UART Baud Rate",
+                       update_uart_baud_rate_cmd, 6, true,
+                       status_rsp, 1, true },
+       { 0x01c, "Write SCO PCM Int Param",
+                       write_sco_pcm_int_param_cmd, 5, true,
+                       status_rsp, 1, true },
+       { 0x01d, "Read SCO PCM Int Param",
+                       null_cmd, 0, true,
+                       read_sco_pcm_int_param_rsp, 6, true },
+       { 0x027, "Set Sleepmode Param",
+                       set_sleepmode_param_cmd, 12, true,
+                       status_rsp, 1, true },
+       { 0x028, "Read Sleepmode Param",
+                       null_cmd, 0, true,
+                       read_sleepmode_param_rsp, 13, true },
        { 0x02e, "Download Minidriver",
                        null_cmd, 0, true,
                        status_rsp, 1, true },
+       { 0x034, "Enable Radio",
+                       enable_radio_cmd, 1, true,
+                       status_rsp, 1, true },
        { 0x03b, "Enable USB HID Emulation",
                        enable_usb_hid_emulation_cmd, 1, true,
                        status_rsp, 1, true },
-       { 0x045, "Write UART Clock Setting" },
+       { 0x044, "Read UART Clock Setting",
+                       null_cmd, 0, true,
+                       read_uart_clock_setting_rsp, 1, true },
+       { 0x045, "Write UART Clock Setting",
+                       write_uart_clock_setting_cmd, 1, true,
+                       status_rsp, 1, true },
        { 0x04c, "Write RAM",
                        write_ram_cmd, 4, false,
                        status_rsp, 1, true },
+       { 0x04d, "Read RAM",
+                       read_ram_cmd, 5, true,
+                       read_ram_rsp, 1, false },
        { 0x04e, "Launch RAM",
                        launch_ram_cmd, 4, true,
                        status_rsp, 1, true },
        { 0x05a, "Read VID PID",
                        null_cmd, 0, true,
                        read_vid_pid_rsp, 5, true },
+       { 0x057, "Write High Priority Connection" },
+       { 0x06d, "Write I2SPCM Interface Param" },
+       { 0x06e, "Read Controller Features",
+                       null_cmd, 0, true,
+                       read_controller_features_rsp, 9, true },
        { 0x079, "Read Verbose Config Version Info",
                        null_cmd, 0, true,
                        read_verbose_version_info_rsp, 7, true },
index 1a21592..595b6a7 100755 (executable)
@@ -2134,6 +2134,255 @@ struct bt_hci_rsp_le_read_max_data_length {
        uint16_t max_rx_time;
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_LE_READ_PHY                 0x2030
+struct bt_hci_cmd_le_read_phy {
+       uint16_t handle;
+} __attribute__((packed));
+struct bt_hci_rsp_le_read_phy {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  tx_phy;
+       uint8_t  rx_phy;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_DEFAULT_PHY          0x2031
+struct bt_hci_cmd_le_set_default_phy {
+       uint8_t  all_phys;
+       uint8_t  tx_phys;
+       uint8_t  rx_phys;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_PHY                  0x2032
+struct bt_hci_cmd_le_set_phy {
+       uint16_t handle;
+       uint8_t  all_phys;
+       uint8_t  tx_phys;
+       uint8_t  rx_phys;
+       uint16_t phy_opts;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_ENHANCED_RECEIVER_TEST                   0x2033
+struct bt_hci_cmd_le_enhanced_receiver_test {
+       uint8_t rx_channel;
+       uint8_t phy;
+       uint8_t modulation_index;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_ENHANCED_TRANSMITTER_TEST                        0x2034
+struct bt_hci_cmd_le_enhanced_transmitter_test {
+       uint8_t tx_channel;
+       uint8_t data_len;
+       uint8_t payload;
+       uint8_t phy;
+} __attribute__((packed));
+
+#define BT_HCI_CMD_LE_SET_ADV_SET_RAND_ADDR                    0x2035
+struct bt_hci_cmd_le_set_adv_set_rand_addr {
+       uint8_t  handle;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_PARAMS                       0x2036
+struct bt_hci_cmd_le_set_ext_adv_params {
+       uint8_t  handle;
+       uint16_t evt_properties;
+       uint8_t  min_interval[3];
+       uint8_t  max_interval[3];
+       uint8_t  channel_map;
+       uint8_t  own_addr_type;
+       uint8_t  peer_addr_type;
+       uint8_t  peer_addr[6];
+       uint8_t  filter_policy;
+       uint8_t  tx_power;
+       uint8_t  primary_phy;
+       uint8_t  secondary_max_skip;
+       uint8_t  secondary_phy;
+       uint8_t  sid;
+       uint8_t  notif_enable;
+} __attribute__ ((packed));
+struct bt_hci_rsp_le_set_ext_adv_params {
+       uint8_t  status;
+       uint8_t  tx_power;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_DATA                 0x2037
+struct bt_hci_cmd_le_set_ext_adv_data {
+       uint8_t  handle;
+       uint8_t  operation;
+       uint8_t  fragment_preference;
+       uint8_t  data_len;
+       uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_RSP_DATA                    0x2038
+struct bt_hci_cmd_le_set_ext_scan_rsp_data {
+       uint8_t  handle;
+       uint8_t  operation;
+       uint8_t  fragment_preference;
+       uint8_t  data_len;
+       uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_ADV_ENABLE                       0x2039
+struct bt_hci_cmd_le_set_ext_adv_enable {
+       uint8_t  enable;
+       uint8_t  num_of_sets;
+} __attribute__ ((packed));
+struct bt_hci_cmd_ext_adv_set {
+       uint8_t  handle;
+       uint16_t duration;
+       uint8_t  max_events;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_MAX_ADV_DATA_LEN                    0x203a
+struct bt_hci_rsp_le_read_max_adv_data_len {
+       uint8_t  status;
+       uint16_t max_len;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_NUM_SUPPORTED_ADV_SETS                      0x203b
+struct bt_hci_rsp_le_read_num_supported_adv_sets {
+       uint8_t  status;
+       uint8_t  num_of_sets;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_ADV_SET                   0x203c
+struct bt_hci_cmd_le_remove_adv_set {
+       uint8_t  handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_ADV_SETS                   0x203d
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_PARAMS                  0x203e
+struct bt_hci_cmd_le_set_periodic_adv_params {
+       uint8_t  handle;
+       uint16_t min_interval;
+       uint16_t max_interval;
+       uint16_t properties;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_DATA                    0x203f
+struct bt_hci_cmd_le_set_periodic_adv_data {
+       uint8_t  handle;
+       uint8_t  operation;
+       uint8_t  data_len;
+       uint8_t  data[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PERIODIC_ADV_ENABLE                  0x2040
+struct bt_hci_cmd_le_set_periodic_adv_enable {
+       uint8_t  enable;
+       uint8_t  handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS              0x2041
+struct bt_hci_cmd_le_set_ext_scan_params {
+       uint8_t  own_addr_type;
+       uint8_t  filter_policy;
+       uint8_t  num_phys;
+       uint8_t  data[0];
+} __attribute__ ((packed));
+struct bt_hci_le_scan_phy {
+       uint8_t  type;
+       uint16_t interval;
+       uint16_t window;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EXT_SCAN_ENABLE              0x2042
+struct bt_hci_cmd_le_set_ext_scan_enable {
+       uint8_t  enable;
+       uint8_t  filter_dup;
+       uint16_t duration;
+       uint16_t period;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_EXT_CREATE_CONN          0x2043
+struct bt_hci_cmd_le_ext_create_conn {
+       uint8_t  filter_policy;
+       uint8_t  own_addr_type;
+       uint8_t  peer_addr_type;
+       uint8_t  peer_addr[6];
+       uint8_t  phys;
+       uint8_t  data[0];
+} __attribute__ ((packed));
+struct bt_hci_le_ext_create_conn {
+       uint16_t scan_interval;
+       uint16_t scan_window;
+       uint16_t min_interval;
+       uint16_t max_interval;
+       uint16_t latency;
+       uint16_t supv_timeout;
+       uint16_t min_length;
+       uint16_t max_length;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_CREATE_SYNC         0x2044
+struct bt_hci_cmd_le_periodic_adv_create_sync {
+       uint8_t  filter_policy;
+       uint8_t  sid;
+       uint8_t  addr_type;
+       uint8_t  addr[6];
+       uint16_t skip;
+       uint16_t sync_timeout;
+       uint8_t  unused;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_CREATE_SYNC_CANCEL          0x2045
+
+#define BT_HCI_CMD_LE_PERIODIC_ADV_TERM_SYNC           0x2046
+struct bt_hci_cmd_le_periodic_adv_term_sync {
+       uint16_t sync_handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_ADD_DEV_PERIODIC_ADV_LIST                0x2047
+struct bt_hci_cmd_le_add_dev_periodic_adv_list {
+       uint8_t  addr_type;
+       uint8_t  addr[6];
+       uint8_t  sid;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_REMOVE_DEV_PERIODIC_ADV_LIST             0x2048
+struct bt_hci_cmd_le_remove_dev_periodic_adv_list {
+       uint8_t  addr_type;
+       uint8_t  addr[6];
+       uint8_t  sid;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_CLEAR_PERIODIC_ADV_LIST          0x2049
+
+#define BT_HCI_CMD_LE_READ_PERIODIC_ADV_LIST_SIZE              0x204a
+struct bt_hci_rsp_le_read_dev_periodic_adv_list_size {
+       uint8_t  status;
+       uint8_t  list_size;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_TX_POWER            0x204b
+struct bt_hci_rsp_le_read_tx_power {
+       uint8_t  status;
+       uint8_t  min_tx_power;
+       uint8_t  max_tx_power;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_RF_PATH_COMPENSATION                0x204c
+struct bt_hci_rsp_le_read_rf_path_comp {
+       uint8_t  status;
+       uint16_t rf_tx_path_comp;
+       uint16_t rf_rx_path_comp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_WRITE_RF_PATH_COMPENSATION               0x204d
+struct bt_hci_cmd_le_write_rf_path_comp {
+       uint16_t rf_tx_path_comp;
+       uint16_t rf_rx_path_comp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_PRIV_MODE            0x204e
+struct bt_hci_cmd_le_set_priv_mode {
+       uint8_t  peer_id_addr_type;
+       uint8_t  peer_id_addr[6];
+       uint8_t  priv_mode;
+} __attribute__ ((packed));
+
 #define BT_HCI_EVT_INQUIRY_COMPLETE            0x01
 struct bt_hci_evt_inquiry_complete {
        uint8_t  status;
@@ -2736,6 +2985,54 @@ struct bt_hci_evt_le_direct_adv_report {
        int8_t   rssi;
 } __attribute__ ((packed));
 
+#define BT_HCI_EVT_LE_PHY_UPDATE_COMPLETE      0x0c
+struct bt_hci_evt_le_phy_update_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  tx_phy;
+       uint8_t  rx_phy;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_EXT_ADV_REPORT   0x0d
+struct bt_hci_evt_le_ext_adv_report {
+       uint8_t  num_reports;
+} __attribute__ ((packed));
+struct bt_hci_le_ext_adv_report {
+       uint16_t event_type;
+       uint8_t  addr_type;
+       uint8_t  addr[6];
+       uint8_t  primary_phy;
+       uint8_t  secondary_phy;
+       uint8_t  sid;
+       uint8_t  tx_power;
+       int8_t   rssi;
+       uint16_t interval;
+       uint8_t  direct_addr_type;
+       uint8_t  direct_addr[6];
+       uint8_t  data_len;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_ADV_SET_TERM             0x12
+struct bt_hci_evt_le_adv_set_term {
+       uint8_t  status;
+       uint8_t  handle;
+       uint16_t conn_handle;
+       uint8_t  num_evts;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_SCAN_REQ_RECEIVED                0x13
+struct bt_hci_evt_le_scan_req_received {
+       uint8_t  handle;
+       uint8_t  scanner_addr_type;
+       uint8_t  scanner_addr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_CHAN_SELECT_ALG          0x14
+struct bt_hci_evt_le_chan_select_alg {
+       uint16_t handle;
+       uint8_t  algorithm;
+} __attribute__ ((packed));
+
 #define BT_HCI_ERR_SUCCESS                     0x00
 #define BT_HCI_ERR_UNKNOWN_COMMAND             0x01
 #define BT_HCI_ERR_UNKNOWN_CONN_ID             0x02
index 4262e43..7c8de2c 100755 (executable)
@@ -1130,11 +1130,18 @@ static int server_fd = -1;
 void control_server(const char *path)
 {
        struct sockaddr_un addr;
+       size_t len;
        int fd;
 
        if (server_fd >= 0)
                return;
 
+       len = strlen(path);
+       if (len > sizeof(addr.sun_path) - 1) {
+               fprintf(stderr, "Socket name too long\n");
+               return;
+       }
+
        unlink(path);
 
        fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -1145,7 +1152,7 @@ void control_server(const char *path)
 
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
-       strcpy(addr.sun_path, path);
+       strncpy(addr.sun_path, path, len);
 
        if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                perror("Failed to bind server socket");
index 1ca9a95..ce624be 100755 (executable)
@@ -202,6 +202,65 @@ static void read_version_rsp(const void *data, uint8_t size)
        print_field("Firmware patch: %u", fw_patch);
 }
 
+static void set_uart_baudrate_cmd(const void *data, uint8_t size)
+{
+       uint8_t baudrate = get_u8(data);
+       const char *str;
+
+       switch (baudrate) {
+       case 0x00:
+               str = "9600 Baud";
+               break;
+       case 0x01:
+               str = "19200 Baud";
+               break;
+       case 0x02:
+               str = "38400 Baud";
+               break;
+       case 0x03:
+               str = "57600 Baud";
+               break;
+       case 0x04:
+               str = "115200 Baud";
+               break;
+       case 0x05:
+               str = "230400 Baud";
+               break;
+       case 0x06:
+               str = "460800 Baud";
+               break;
+       case 0x07:
+               str = "921600 Baud";
+               break;
+       case 0x08:
+               str = "1843200 Baud";
+               break;
+       case 0x09:
+               str = "3250000 baud";
+               break;
+       case 0x0a:
+               str = "2000000 baud";
+               break;
+       case 0x0b:
+               str = "3000000 baud";
+               break;
+       case 0x0c:
+               str = "3714286 baud";
+               break;
+       case 0x0d:
+               str = "4333333 baud";
+               break;
+       case 0x0e:
+               str = "6500000 baud";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Baudrate: %s (0x%2.2x)", str, baudrate);
+}
+
 static void secure_send_cmd(const void *data, uint8_t size)
 {
        uint8_t type = get_u8(data);
@@ -432,7 +491,9 @@ static const struct vendor_ocf vendor_ocf_table[] = {
        { 0x005, "Read Version",
                        null_cmd, 0, true,
                        read_version_rsp, 10, true },
-       { 0x006, "Set UART Baudrate" },
+       { 0x006, "Set UART Baudrate",
+                       set_uart_baudrate_cmd, 1, true,
+                       status_rsp, 1, true },
        { 0x007, "Enable LPM" },
        { 0x008, "PCM Write Configuration" },
        { 0x009, "Secure Send",
index f4b54af..c85934a 100755 (executable)
 #include <inttypes.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 
 #include "src/shared/util.h"
 #include "bt.h"
 #include "packet.h"
 #include "display.h"
 #include "l2cap.h"
-#include "uuid.h"
 #include "keys.h"
 #include "sdp.h"
 #include "avctp.h"
@@ -573,9 +573,12 @@ static void print_le_conn_result(uint16_t result)
        case 0x0009:
                str = "Connection refused - Invalid Source CID";
                break;
-       case 0x0010:
+       case 0x000a:
                str = "Connection refused - Source CID already allocated";
                break;
+       case 0x000b:
+               str = "Connection refused - unacceptable parameters";
+               break;
        default:
                str = "Reserved";
                break;
@@ -2003,11 +2006,11 @@ static void print_uuid(const char *label, const void *data, uint16_t size)
 
        switch (size) {
        case 2:
-               str = uuid16_to_str(get_le16(data));
+               str = bt_uuid16_to_str(get_le16(data));
                print_field("%s: %s (0x%4.4x)", label, str, get_le16(data));
                break;
        case 4:
-               str = uuid32_to_str(get_le32(data));
+               str = bt_uuid32_to_str(get_le32(data));
                print_field("%s: %s (0x%8.8x)", label, str, get_le32(data));
                break;
        case 16:
@@ -2015,7 +2018,7 @@ static void print_uuid(const char *label, const void *data, uint16_t size)
                                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);
+               str = bt_uuidstr_to_str(uuidstr);
                print_field("%s: %s (%s)", label, str, uuidstr);
                break;
        default:
@@ -2055,7 +2058,7 @@ static void print_data_list(const char *label, uint8_t length,
 
 static void print_attribute_info(uint16_t type, const void *data, uint16_t len)
 {
-       const char *str = uuid16_to_str(type);
+       const char *str = bt_uuid16_to_str(type);
 
        print_field("%s: %s (0x%4.4x)", "Attribute type", str, type);
 
@@ -2671,7 +2674,7 @@ static void print_smp_oob_data(uint8_t oob_data)
 
 static void print_smp_auth_req(uint8_t auth_req)
 {
-       const char *bond, *mitm, *sc, *kp;
+       const char *bond, *mitm, *sc, *kp, *ct2;
 
        switch (auth_req & 0x03) {
        case 0x00:
@@ -2685,23 +2688,28 @@ static void print_smp_auth_req(uint8_t auth_req)
                break;
        }
 
-       if ((auth_req & 0x04))
+       if (auth_req & 0x04)
                mitm = "MITM";
        else
                mitm = "No MITM";
 
-       if ((auth_req & 0x08))
+       if (auth_req & 0x08)
                sc = "SC";
        else
                sc = "Legacy";
 
-       if ((auth_req & 0x10))
+       if (auth_req & 0x10)
                kp = "Keypresses";
        else
                kp = "No Keypresses";
 
-       print_field("Authentication requirement: %s, %s, %s, %s (0x%2.2x)",
-                                               bond, mitm, sc, kp, auth_req);
+       if (auth_req & 0x20)
+               ct2 = ", CT2";
+       else
+               ct2 = "";
+
+       print_field("Authentication requirement: %s, %s, %s, %s%s (0x%2.2x)",
+                                       bond, mitm, sc, kp, ct2, auth_req);
 }
 
 static void print_smp_key_dist(const char *label, uint8_t dist)
index 6212f79..c5e1105 100755 (executable)
@@ -506,6 +506,10 @@ static const struct llcp_data llcp_table[] = {
        { 0x13, "LL_PING_RSP",              null_pdu,           0, true },
        { 0x14, "LL_LENGTH_REQ",            NULL,               8, true },
        { 0x15, "LL_LENGTH_RSP",            NULL,               8, true },
+       { 0x16, "LL_PHY_REQ",               NULL,               2, true },
+       { 0x17, "LL_PHY_RSP",               NULL,               2, true },
+       { 0x18, "LL_PHY_UPDATE_IND",        NULL,               4, true },
+       { 0x19, "LL_MIN_USED_CHANNELS_IND", NULL,               2, true },
        { }
 };
 
index e7e6b25..b87b549 100755 (executable)
@@ -821,6 +821,9 @@ static const struct lmp_data lmp_table[] = {
        { LMP_ESC4(32), "LMP_power_control_res", power_control_res, 1, true },
        { LMP_ESC4(33), "LMP_ping_req", ping_req, 0, true },
        { LMP_ESC4(34), "LMP_ping_res", ping_res, 0, true },
+       { LMP_ESC4(35), "LMP_SAM_set_type0" },
+       { LMP_ESC4(36), "LMP_SAM_define_map" },
+       { LMP_ESC4(37), "LMP_SAM_switch" },
        { }
 };
 
index 57830ad..4f8a8e1 100755 (executable)
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
+#include <sys/un.h>
 
 #include "src/shared/mainloop.h"
 #include "src/shared/tty.h"
@@ -69,6 +70,7 @@ static void usage(void)
                "\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"
+               "\t-A, --a2dp             Dump A2DP stream traffic\n"
                "\t-E, --ellisys [ip]     Send Ellisys HCI Injection\n"
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                "\t-C, --count <num>      Save traces by <num> rotation\n"
@@ -89,6 +91,7 @@ static const struct option main_options[] = {
        { "time",    no_argument,       NULL, 't' },
        { "date",    no_argument,       NULL, 'T' },
        { "sco",     no_argument,       NULL, 'S' },
+       { "a2dp",    no_argument,       NULL, 'A' },
        { "ellisys", required_argument, NULL, 'E' },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        { "count",   required_argument, NULL, 'C' },
@@ -124,12 +127,13 @@ int main(int argc, char *argv[])
 
        for (;;) {
                int opt;
+               struct sockaddr_un addr;
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:C:W:vh",
                                                main_options, NULL);
 #else
-               opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
+               opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSAE:vh",
                                                main_options, NULL);
 #endif
                if (opt < 0)
@@ -156,6 +160,10 @@ int main(int argc, char *argv[])
                        analyze_path = optarg;
                        break;
                case 's':
+                       if (strlen(optarg) > sizeof(addr.sun_path) - 1) {
+                               fprintf(stderr, "Socket name too long\n");
+                               return EXIT_FAILURE;
+                       }
                        control_server(optarg);
                        break;
                case 'p':
@@ -184,6 +192,9 @@ int main(int argc, char *argv[])
                case 'S':
                        filter_mask |= PACKET_FILTER_SHOW_SCO_DATA;
                        break;
+               case 'A':
+                       filter_mask |= PACKET_FILTER_SHOW_A2DP_STREAM;
+                       break;
                case 'E':
                        ellisys_server = optarg;
                        ellisys_port = 24352;
index 259b805..eeed1f2 100755 (executable)
@@ -39,6 +39,7 @@
 #include <sys/socket.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 #include "lib/hci.h"
 #include "lib/hci_lib.h"
 
@@ -49,7 +50,6 @@
 #include "ll.h"
 #include "hwdb.h"
 #include "keys.h"
-#include "uuid.h"
 #include "l2cap.h"
 #include "control.h"
 #include "vendor.h"
@@ -58,6 +58,7 @@
 #include "packet.h"
 
 #define COLOR_CHANNEL_LABEL            COLOR_WHITE
+#define COLOR_FRAME_LABEL              COLOR_WHITE
 #define COLOR_INDEX_LABEL              COLOR_WHITE
 #define COLOR_TIMESTAMP                        COLOR_YELLOW
 
@@ -220,6 +221,11 @@ static uint8_t get_type(uint16_t handle)
        return 0xff;
 }
 
+bool packet_has_filter(unsigned long filter)
+{
+       return filter_mask & filter;
+}
+
 void packet_set_filter(unsigned long filter)
 {
        filter_mask = filter;
@@ -259,6 +265,17 @@ void packet_select_index(uint16_t index)
 
 #define print_space(x) printf("%*c", (x), ' ');
 
+#define MAX_INDEX 16
+
+struct index_data {
+       uint8_t  type;
+       uint8_t  bdaddr[6];
+       uint16_t manufacturer;
+       size_t  frame;
+};
+
+static struct index_data index_list[MAX_INDEX];
+
 static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
                                        uint16_t index, const char *channel,
                                        const char *color, const char *label,
@@ -267,6 +284,7 @@ static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
        int col = num_columns();
        char line[256], ts_str[96];
        int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
+       static size_t last_frame;
 
        if (channel) {
                if (use_color()) {
@@ -280,6 +298,20 @@ static void print_packet(struct timeval *tv, struct ucred *cred, char ident,
                        ts_pos += n;
                        ts_len += n;
                }
+       } else if (index != HCI_DEV_NONE &&
+                               index_list[index].frame != last_frame) {
+               if (use_color()) {
+                       n = sprintf(ts_str + ts_pos, "%s", COLOR_FRAME_LABEL);
+                       if (n > 0)
+                               ts_pos += n;
+               }
+
+               n = sprintf(ts_str + ts_pos, " #%zu", index_list[index].frame);
+               if (n > 0) {
+                       ts_pos += n;
+                       ts_len += n;
+               }
+               last_frame = index_list[index].frame;
        }
 
        if ((filter_mask & PACKET_FILTER_SHOW_INDEX) &&
@@ -464,12 +496,16 @@ static const struct {
        { 0x39, "Connection Rejected due to No Suitable Channel Found"  },
        { 0x3a, "Controller Busy"                                       },
        { 0x3b, "Unacceptable Connection Parameters"                    },
-       { 0x3c, "Directed Advertising Timeout"                          },
+       { 0x3c, "Advertising Timeout"                                   },
        { 0x3d, "Connection Terminated due to MIC Failure"              },
        { 0x3e, "Connection Failed to be Established"                   },
        { 0x3f, "MAC Connection Failed"                                 },
        { 0x40, "Coarse Clock Adjustment Rejected "
                "but Will Try to Adjust Using Clock Dragging"           },
+       { 0x41, "Type0 Submap Not Defined"                              },
+       { 0x42, "Unknown Advertising Identifier"                        },
+       { 0x43, "Limit Reached"                                         },
+       { 0x44, "Operation Cancelled by Host"                           },
        { }
 };
 
@@ -521,6 +557,25 @@ void packet_print_error(const char *label, uint8_t error)
        print_error(label, error);
 }
 
+static void print_enable(const char *label, uint8_t enable)
+{
+       const char *str;
+
+       switch (enable) {
+       case 0x00:
+               str = "Disabled";
+               break;
+       case 0x01:
+               str = "Enabled";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s: %s (0x%2.2x)", label, str, enable);
+}
+
 static void print_addr_type(const char *label, uint8_t addr_type)
 {
        const char *str;
@@ -1026,90 +1081,10 @@ static void print_dev_class(const uint8_t *dev_class)
                                "  Unknown service class (0x%2.2x)", mask);
 }
 
-static const struct {
-       uint16_t val;
-       bool generic;
-       const char *str;
-} appearance_table[] = {
-       {    0, true,  "Unknown"                },
-       {   64, true,  "Phone"                  },
-       {  128, true,  "Computer"               },
-       {  192, true,  "Watch"                  },
-       {  193, false, "Sports Watch"           },
-       {  256, true,  "Clock"                  },
-       {  320, true,  "Display"                },
-       {  384, true,  "Remote Control"         },
-       {  448, true,  "Eye-glasses"            },
-       {  512, true,  "Tag"                    },
-       {  576, true,  "Keyring"                },
-       {  640, true,  "Media Player"           },
-       {  704, true,  "Barcode Scanner"        },
-       {  768, true,  "Thermometer"            },
-       {  769, false, "Thermometer: Ear"       },
-       {  832, true,  "Heart Rate Sensor"      },
-       {  833, false, "Heart Rate Belt"        },
-       {  896, true,  "Blood Pressure"         },
-       {  897, false, "Blood Pressure: Arm"    },
-       {  898, false, "Blood Pressure: Wrist"  },
-       {  960, true,  "Human Interface Device" },
-       {  961, false, "Keyboard"               },
-       {  962, false, "Mouse"                  },
-       {  963, false, "Joystick"               },
-       {  964, false, "Gamepad"                },
-       {  965, false, "Digitizer Tablet"       },
-       {  966, false, "Card Reader"            },
-       {  967, false, "Digital Pen"            },
-       {  968, false, "Barcode Scanner"        },
-       { 1024, true,  "Glucose Meter"          },
-       { 1088, true,  "Running Walking Sensor"                 },
-       { 1089, false, "Running Walking Sensor: In-Shoe"        },
-       { 1090, false, "Running Walking Sensor: On-Shoe"        },
-       { 1091, false, "Running Walking Sensor: On-Hip"         },
-       { 1152, true,  "Cycling"                                },
-       { 1153, false, "Cycling: Cycling Computer"              },
-       { 1154, false, "Cycling: Speed Sensor"                  },
-       { 1155, false, "Cycling: Cadence Sensor"                },
-       { 1156, false, "Cycling: Power Sensor"                  },
-       { 1157, false, "Cycling: Speed and Cadence Sensor"      },
-       { 1216, true,  "Undefined"                              },
-
-       { 3136, true,  "Pulse Oximeter"                         },
-       { 3137, false, "Pulse Oximeter: Fingertip"              },
-       { 3138, false, "Pulse Oximeter: Wrist Worn"             },
-       { 3200, true,  "Weight Scale"                           },
-       { 3264, true,  "Undefined"                              },
-
-       { 5184, true,  "Outdoor Sports Activity"                },
-       { 5185, false, "Location Display Device"                },
-       { 5186, false, "Location and Navigation Display Device" },
-       { 5187, false, "Location Pod"                           },
-       { 5188, false, "Location and Navigation Pod"            },
-       { 5248, true,  "Undefined"                              },
-       { }
-};
-
 static void print_appearance(uint16_t appearance)
 {
-       const char *str = NULL;
-       int i, type = 0;
-
-       for (i = 0; appearance_table[i].str; i++) {
-               if (appearance_table[i].generic) {
-                       if (appearance < appearance_table[i].val)
-                               break;
-                       type = i;
-               }
-
-               if (appearance_table[i].val == appearance) {
-                       str = appearance_table[i].str;
-                       break;
-               }
-       }
-
-       if (!str)
-               str = appearance_table[type].str;
-
-       print_field("Appearance: %s (0x%4.4x)", str, appearance);
+       print_field("Appearance: %s (0x%4.4x)", bt_appear_to_str(appearance),
+                                                               appearance);
 }
 
 static void print_num_broadcast_retrans(uint8_t num_retrans)
@@ -1155,27 +1130,9 @@ static void print_power_type(uint8_t type)
 
 static void print_power_level(int8_t level, const char *type)
 {
-       print_field("TX power%s%s%s: %d dBm",
-               type ? " (" : "", type ? type : "", type ? ")" : "", level);
-}
-
-static void print_sync_flow_control(uint8_t enable)
-{
-       const char *str;
-
-       switch (enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Flow control: %s (0x%2.2x)", str, enable);
+       print_field("TX power%s%s%s: %d dbm (0x%2.2x)",
+               type ? " (" : "", type ? type : "", type ? ")" : "",
+                                                               level, level);
 }
 
 static void print_host_flow_control(uint8_t enable)
@@ -1467,44 +1424,6 @@ static void print_pscan_type(uint8_t type)
        print_field("Type: %s (0x%2.2x)", str, type);
 }
 
-static void print_afh_mode(uint8_t mode)
-{
-       const char *str;
-
-       switch (mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_erroneous_reporting(uint8_t mode)
-{
-       const char *str;
-
-       switch (mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
 static void print_loopback_mode(uint8_t mode)
 {
        const char *str;
@@ -1527,63 +1446,6 @@ static void print_loopback_mode(uint8_t mode)
        print_field("Mode: %s (0x%2.2x)", str, mode);
 }
 
-static void print_simple_pairing_mode(uint8_t mode)
-{
-       const char *str;
-
-       switch (mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_ssp_debug_mode(uint8_t mode)
-{
-       const char *str;
-
-       switch (mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Debug mode: %s (0x%2.2x)", str, mode);
-}
-
-static void print_secure_conn_support(uint8_t support)
-{
-       const char *str;
-
-       switch (support) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Support: %s (0x%2.2x)", str, support);
-}
-
 static void print_auth_payload_timeout(uint16_t timeout)
 {
        print_field("Timeout: %d msec (0x%4.4x)",
@@ -1752,25 +1614,6 @@ static void print_link_type(uint8_t link_type)
        print_field("Link type: %s (0x%2.2x)", str, link_type);
 }
 
-static void print_encr_mode(uint8_t encr_mode)
-{
-       const char *str;
-
-       switch (encr_mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Encryption: %s (0x%2.2x)", str, encr_mode);
-}
-
 static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle)
 {
        const char *str;
@@ -2209,25 +2052,6 @@ static void print_flow_spec(const char *label, const uint8_t *data)
        print_field("  Flush timeout: 0x%8.8x", get_le32(data + 12));
 }
 
-static void print_short_range_mode(uint8_t mode)
-{
-       const char *str;
-
-       switch (mode) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Short range mode: %s (0x%2.2x)", str, mode);
-}
-
 static void print_amp_status(uint8_t amp_status)
 {
        const char *str;
@@ -2272,7 +2096,7 @@ static void print_num_reports(uint8_t num_reports)
        print_field("Num reports: %d", num_reports);
 }
 
-static void print_adv_event_type(uint8_t type)
+static void print_adv_event_type(const char *label, uint8_t type)
 {
        const char *str;
 
@@ -2297,7 +2121,70 @@ static void print_adv_event_type(uint8_t type)
                break;
        }
 
-       print_field("Event type: %s (0x%2.2x)", str, type);
+       print_field("%s: %s (0x%2.2x)", label, str, type);
+}
+
+static void print_adv_channel_map(const char *label, uint8_t value)
+{
+       const char *str;
+
+       switch (value) {
+       case 0x01:
+               str = "37";
+               break;
+       case 0x02:
+               str = "38";
+               break;
+       case 0x03:
+               str = "37, 38";
+               break;
+       case 0x04:
+               str = "39";
+               break;
+       case 0x05:
+               str = "37, 39";
+               break;
+       case 0x06:
+               str = "38, 39";
+               break;
+       case 0x07:
+               str = "37, 38, 39";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s: %s (0x%2.2x)", label, str, value);
+}
+
+static void print_adv_filter_policy(const char *label, uint8_t value)
+{
+       const char *str;
+
+       switch (value) {
+       case 0x00:
+               str = "Allow Scan Request from Any, "
+                       "Allow Connect Request from Any";
+               break;
+       case 0x01:
+               str = "Allow Scan Request from White List Only, "
+                       "Allow Connect Request from Any";
+               break;
+       case 0x02:
+               str = "Allow Scan Request from Any, "
+                       "Allow Connect Request from White List Only";
+               break;
+       case 0x03:
+               str = "Allow Scan Request from White List Only, "
+                       "Allow Connect Request from White List Only";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s: %s (0x%2.2x)", label, str, value);
 }
 
 static void print_rssi(int8_t rssi)
@@ -2335,6 +2222,12 @@ static void print_window(uint16_t window)
        print_slot_625("Window", window);
 }
 
+static void print_conn_latency(const char *label, uint16_t value)
+{
+       print_field("%s: %u (0x%4.4x)", label, le16_to_cpu(value),
+                                                       le16_to_cpu(value));
+}
+
 static void print_role(uint8_t role)
 {
        const char *str;
@@ -2467,6 +2360,9 @@ void packet_print_version(const char *label, uint8_t version,
        case 0x08:
                str = "Bluetooth 4.2";
                break;
+       case 0x09:
+               str = "Bluetooth 5.0";
+               break;
        default:
                str = "Reserved";
                break;
@@ -2706,6 +2602,7 @@ static const struct features_data features_page2[] = {
        {  6, "Coarse Clock Adjustment"                 },
        {  8, "Secure Connections (Controller Support)" },
        {  9, "Ping"                                    },
+       { 10, "Slot Availability Mask"                  },
        { 11, "Train nudging"                           },
        { }
 };
@@ -2719,6 +2616,15 @@ static const struct features_data features_le[] = {
        {  5, "LE Data Packet Length Extension"         },
        {  6, "LL Privacy"                              },
        {  7, "Extended Scanner Filter Policies"        },
+       {  8, "LE 2M PHY"                               },
+       {  9, "Stable Modulation Index - Transmitter"   },
+       { 10, "Stable Modulation Index - Receiver"      },
+       { 11, "LE Coded PHY"                            },
+       { 12, "LE Extended Advertising"                 },
+       { 13, "LE Periodic Advertising"                 },
+       { 14, "Channel Selection Algorithm #2"          },
+       { 15, "LE Power Class 1"                        },
+       { 16, "Minimum Number of Used Channels Procedure"},
        { }
 };
 
@@ -3078,6 +2984,7 @@ static const struct {
        { 21, "Connectionless Slave Broadcast Channel Map Change"       },
        { 22, "Inquiry Response Notification"                           },
        { 23, "Authenticated Payload Timeout Expired"                   },
+       { 24, "SAM Status Change"                                       },
        { }
 };
 
@@ -3120,6 +3027,15 @@ static const struct {
        {  8, "LE Generate DHKey Complete"              },
        {  9, "LE Enhanced Connection Complete"         },
        { 10, "LE Direct Advertising Report"            },
+       { 11, "LE PHY Update Complete"                  },
+       { 12, "LE Extended Advertising Report"          },
+       { 13, "LE Periodic Advertising Sync Established"},
+       { 14, "LE Periodic Advertising Report"          },
+       { 15, "LE Periodic Advertising Sync Lost"       },
+       { 16, "LE Extended Scan Timeout"                },
+       { 17, "LE Extended Advertising Set Terminated"  },
+       { 18, "LE Scan Request Received"                },
+       { 19, "LE Channel Selection Algorithm"          },
        { }
 };
 
@@ -3194,6 +3110,19 @@ static void print_fec(uint8_t fec)
 #define BT_EIR_LE_ROLE                 0x1c
 #define BT_EIR_SSP_HASH_P256           0x1d
 #define BT_EIR_SSP_RANDOMIZER_P256     0x1e
+#define BT_EIR_SERVICE_UUID32          0x1f
+#define BT_EIR_SERVICE_DATA32          0x20
+#define BT_EIR_SERVICE_DATA128         0x21
+#define BT_EIR_LE_SC_CONFIRM_VALUE     0x22
+#define BT_EIR_LE_SC_RANDOM_VALUE      0x23
+#define BT_EIR_URI                     0x24
+#define BT_EIR_INDOOR_POSITIONING      0x25
+#define BT_EIR_TRANSPORT_DISCOVERY     0x26
+#define BT_EIR_LE_SUPPORTED_FEATURES   0x27
+#define BT_EIR_CHANNEL_MAP_UPDATE_IND  0x28
+#define BT_EIR_MESH_PROV               0x29
+#define BT_EIR_MESH_DATA               0x2a
+#define BT_EIR_MESH_BEACON             0x2b
 #define BT_EIR_3D_INFO_DATA            0x3d
 #define BT_EIR_MANUFACTURER_DATA       0xff
 
@@ -3370,7 +3299,7 @@ static void print_uuid16_list(const char *label, const void *data,
 
        for (i = 0; i < count; i++) {
                uint16_t uuid = get_le16(data + (i * 2));
-               print_field("  %s (0x%4.4x)", uuid16_to_str(uuid), uuid);
+               print_field("  %s (0x%4.4x)", bt_uuid16_to_str(uuid), uuid);
        }
 }
 
@@ -3384,7 +3313,7 @@ static void print_uuid32_list(const char *label, const void *data,
 
        for (i = 0; i < count; i++) {
                uint32_t uuid = get_le32(data + (i * 4));
-               print_field("  %s (0x%8.8x)", uuid32_to_str(uuid), uuid);
+               print_field("  %s (0x%8.8x)", bt_uuid32_to_str(uuid), uuid);
        }
 }
 
@@ -3404,7 +3333,7 @@ static void print_uuid128_list(const char *label, const void *data,
                                get_le32(&uuid[12]), get_le16(&uuid[10]),
                                get_le16(&uuid[8]), get_le16(&uuid[6]),
                                get_le32(&uuid[2]), get_le16(&uuid[0]));
-               print_field("  %s (%s)", uuidstr_to_str(uuidstr), uuidstr);
+               print_field("  %s (%s)", bt_uuidstr_to_str(uuidstr), uuidstr);
        }
 }
 
@@ -3431,6 +3360,191 @@ static const struct {
        { }
 };
 
+static const struct {
+       uint8_t bit;
+       const char *str;
+} mesh_oob_table[] = {
+       { 0, "Other"                                                    },
+       { 1, "Electronic / URI"                                         },
+       { 2, "2D machine-readable code"                                 },
+       { 3, "Bar code"                                                 },
+       { 4, "Near Field Communication (NFC)"                           },
+       { 5, "Number"                                                   },
+       { 6, "String"                                                   },
+       { 11, "On box"                                                  },
+       { 12, "Inside box"                                              },
+       { 13, "On piece of paper"                                       },
+       { 14, "Inside manual"                                           },
+       { 15, "On device"                                               },
+       { }
+};
+
+static void print_mesh_beacon(const uint8_t *data, uint8_t len)
+{
+       uint16_t oob;
+       int i;
+
+       print_hex_field("Mesh Beacon", data, len);
+
+       if (len < 1)
+               return;
+
+       switch (data[0]) {
+       case 0x00:
+               print_field("  Unprovisioned Device Beacon (0x00)");
+               if (len < 18) {
+                       packet_hexdump(data + 1, len - 1);
+                       break;
+               }
+
+               print_hex_field("  Device UUID", data + 1, 16);
+
+               oob = get_be16(data + 17);
+               print_field("  OOB Information: 0x%4.4x", oob);
+
+               for (i = 0; mesh_oob_table[i].str; i++) {
+                       if (oob & (1 << mesh_oob_table[i].bit))
+                               print_field("    %s", mesh_oob_table[i].str);
+               }
+
+               if (len < 23) {
+                       packet_hexdump(data + 18, len - 18);
+                       break;
+               }
+
+               print_field("  URI Hash: 0x%8.8x", get_be32(data + 19));
+               packet_hexdump(data + 23, len - 23);
+               break;
+       case 0x01:
+               print_field("  Secure Network Beacon (0x01)");
+               if (len < 22) {
+                       packet_hexdump(data + 1, len - 1);
+                       break;
+               }
+
+               print_field("  Flags: 0x%2.2x", data[0]);
+
+               if (data[1] & 0x01)
+                       print_field("    Key Refresh");
+
+               if (data[1] & 0x02)
+                       print_field("    IV Update");
+
+               print_hex_field("  Network Id", data + 2, 8);
+               print_field("  IV Index: 0x%08x", get_be32(data + 10));
+               print_hex_field("  Authentication Value", data + 14, 8);
+               packet_hexdump(data + 22, len - 22);
+               break;
+       default:
+               print_field("  Invalid Beacon (0x%02x)", data[0]);
+               packet_hexdump(data, len);
+               break;
+       }
+}
+
+static void print_mesh_prov(const uint8_t *data, uint8_t len)
+{
+       print_hex_field("Mesh Provisioning", data, len);
+
+       if (len < 6) {
+               packet_hexdump(data, len);
+               return;
+       }
+
+       print_field("  Link ID: 0x%08x", get_be32(data));
+       print_field("  Transaction Number: %u", data[4]);
+
+       data += 5;
+       len -= 5;
+
+       switch (data[0] & 0x03) {
+       case 0x00:
+               print_field("  Transaction Start (0x00)");
+               if (len < 5) {
+                       packet_hexdump(data + 1, len - 1);
+                       return;
+               }
+               print_field("  SeqN: %u", data[0] & 0xfc >> 2);
+               print_field("  TotalLength: %u", get_be16(data + 1));
+               print_field("  FCS: 0x%2.2x", data[3]);
+               print_hex_field("  Data", data + 4, len - 4);
+               packet_hexdump(data + 5, len - 5);
+               break;
+       case 0x01:
+               print_field("  Transaction Acknowledgment (0x01)");
+               packet_hexdump(data + 1, len - 1);
+               break;
+       case 0x02:
+               print_field("  Transaction Continuation (0x02)");
+               print_field("  SegmentIndex: %u", data[0] >> 2);
+               if (len < 2) {
+                       packet_hexdump(data + 1, len - 1);
+                       return;
+               }
+               print_hex_field("  Data", data + 1, len - 1);
+               packet_hexdump(data + 2, len - 2);
+               break;
+       case 0x03:
+               print_field("  Provisioning Bearer Control (0x03)");
+               switch (data[0] >> 2) {
+               case 0x00:
+                       print_field("  Link Open (0x00)");
+                       if (len < 17) {
+                               packet_hexdump(data + 1, len - 1);
+                               break;
+                       }
+                       print_hex_field("  Device UUID", data, 16);
+                       break;
+               case 0x01:
+                       print_field("  Link Ack (0x01)");
+                       break;
+               case 0x02:
+                       print_field("  Link Close (0x02)");
+                       if (len < 2) {
+                               packet_hexdump(data + 1, len - 1);
+                               break;
+                       }
+
+                       switch (data[1]) {
+                       case 0x00:
+                               print_field("  Reason: Success (0x00)");
+                               break;
+                       case 0x01:
+                               print_field("  Reason: Timeout (0x01)");
+                               break;
+                       case 0x02:
+                               print_field("  Reason: Fail (0x02)");
+                               break;
+                       default:
+                               print_field("  Reason: Unrecognized (0x%2.2x)",
+                                                               data[1]);
+                       }
+                       packet_hexdump(data + 2, len - 2);
+                       break;
+               default:
+                       packet_hexdump(data + 1, len - 1);
+                       break;
+               }
+               break;
+       default:
+               print_field("  Invalid Command (0x%02x)", data[0]);
+               packet_hexdump(data, len);
+               break;
+       }
+}
+
+static void print_mesh_data(const uint8_t *data, uint8_t len)
+{
+       print_hex_field("Mesh Data", data, len);
+
+       if (len < 1)
+               return;
+
+       print_field("  IVI: %u", data[0] >> 7);
+       print_field("  NID: 0x%2.2x", data[0] & 0x7f);
+       packet_hexdump(data + 1, len - 1);
+}
+
 static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
 {
        uint16_t len = 0;
@@ -3655,6 +3769,18 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
                        print_field("  Path Loss Threshold: %d", data[1]);
                        break;
 
+               case BT_EIR_MESH_DATA:
+                       print_mesh_data(data, data_len);
+                       break;
+
+               case BT_EIR_MESH_PROV:
+                       print_mesh_prov(data, data_len);
+                       break;
+
+               case BT_EIR_MESH_BEACON:
+                       print_mesh_beacon(data, data_len);
+                       break;
+
                case BT_EIR_MANUFACTURER_DATA:
                        if (data_len < 2)
                                break;
@@ -3786,16 +3912,6 @@ static int addr2str(const uint8_t *addr, char *str)
                        addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
 }
 
-#define MAX_INDEX 16
-
-struct index_data {
-       uint8_t  type;
-       uint8_t  bdaddr[6];
-       uint16_t manufacturer;
-};
-
-static struct index_data index_list[MAX_INDEX];
-
 void packet_monitor(struct timeval *tv, struct ucred *cred,
                                        uint16_t index, uint16_t opcode,
                                        const void *data, uint16_t size)
@@ -3807,10 +3923,11 @@ void packet_monitor(struct timeval *tv, struct ucred *cred,
        uint16_t manufacturer;
        const char *ident;
 
-       if (index_filter && index_number != index)
-               return;
-
-       index_current = index;
+       if (index != HCI_DEV_NONE) {
+               if (index_filter && index_number != index)
+                       return;
+               index_current = index;
+       }
 
        if (tv && time_offset == ((time_t) -1))
                time_offset = tv->tv_sec;
@@ -4097,7 +4214,7 @@ static void set_conn_encrypt_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_set_conn_encrypt *cmd = data;
 
        print_handle(cmd->handle);
-       print_encr_mode(cmd->encr_mode);
+       print_enable("Encryption", cmd->encr_mode);
 }
 
 static void change_conn_link_key_cmd(const void *data, uint8_t size)
@@ -5044,14 +5161,14 @@ static void read_sync_flow_control_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_sync_flow_control *rsp = data;
 
        print_status(rsp->status);
-       print_sync_flow_control(rsp->enable);
+       print_enable("Flow control", rsp->enable);
 }
 
 static void write_sync_flow_control_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_sync_flow_control *cmd = data;
 
-       print_sync_flow_control(cmd->enable);
+       print_enable("Flow control", cmd->enable);
 }
 
 static void set_host_flow_control_cmd(const void *data, uint8_t size)
@@ -5235,14 +5352,14 @@ static void read_afh_assessment_mode_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_afh_assessment_mode *rsp = data;
 
        print_status(rsp->status);
-       print_afh_mode(rsp->mode);
+       print_enable("Mode", rsp->mode);
 }
 
 static void write_afh_assessment_mode_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_afh_assessment_mode *cmd = data;
 
-       print_afh_mode(cmd->mode);
+       print_enable("Mode", cmd->mode);
 }
 
 static void read_ext_inquiry_response_rsp(const void *data, uint8_t size)
@@ -5274,14 +5391,14 @@ static void read_simple_pairing_mode_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_simple_pairing_mode *rsp = data;
 
        print_status(rsp->status);
-       print_simple_pairing_mode(rsp->mode);
+       print_enable("Mode", rsp->mode);
 }
 
 static void write_simple_pairing_mode_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_simple_pairing_mode *cmd = data;
 
-       print_simple_pairing_mode(cmd->mode);
+       print_enable("Mode", cmd->mode);
 }
 
 static void read_local_oob_data_rsp(const void *data, uint8_t size)
@@ -5313,14 +5430,14 @@ static void read_erroneous_reporting_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_erroneous_reporting *rsp = data;
 
        print_status(rsp->status);
-       print_erroneous_reporting(rsp->mode);
+       print_enable("Mode", rsp->mode);
 }
 
 static void write_erroneous_reporting_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_erroneous_reporting *cmd = data;
 
-       print_erroneous_reporting(cmd->mode);
+       print_enable("Mode", cmd->mode);
 }
 
 static void enhanced_flush_cmd(const void *data, uint8_t size)
@@ -5448,7 +5565,7 @@ static void short_range_mode_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_short_range_mode *cmd = data;
 
        print_phy_handle(cmd->phy_handle);
-       print_short_range_mode(cmd->mode);
+       print_enable("Short range mode", cmd->mode);
 }
 
 static void read_le_host_supported_rsp(const void *data, uint8_t size)
@@ -5558,14 +5675,14 @@ static void read_secure_conn_support_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_secure_conn_support *rsp = data;
 
        print_status(rsp->status);
-       print_secure_conn_support(rsp->support);
+       print_enable("Support", rsp->support);
 }
 
 static void write_secure_conn_support_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_secure_conn_support *cmd = data;
 
-       print_secure_conn_support(cmd->support);
+       print_enable("Support", cmd->support);
 }
 
 static void read_auth_payload_timeout_cmd(const void *data, uint8_t size)
@@ -5856,7 +5973,7 @@ static void read_afh_channel_map_rsp(const void *data, uint8_t size)
 
        print_status(rsp->status);
        print_handle(rsp->handle);
-       print_afh_mode(rsp->mode);
+       print_enable("Mode", rsp->mode);
        print_channel_map(rsp->map);
 }
 
@@ -6028,24 +6145,9 @@ static void get_mws_transport_config_rsp(const void *data, uint8_t size)
 static void set_triggered_clock_capture_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_set_triggered_clock_capture *cmd = data;
-       const char *str;
 
        print_handle(cmd->handle);
-
-       switch (cmd->enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Capture: %s (0x%2.2x)", str, cmd->enable);
-
+       print_enable("Capture", cmd->enable);
        print_clock_type(cmd->type);
        print_lpo_allowed(cmd->lpo_allowed);
        print_field("Clock captures to filter: %u", cmd->num_filter);
@@ -6070,7 +6172,7 @@ static void write_ssp_debug_mode_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data;
 
-       print_ssp_debug_mode(cmd->mode);
+       print_enable("Debug Mode", cmd->mode);
 }
 
 static void le_set_event_mask_cmd(const void *data, uint8_t size)
@@ -6138,59 +6240,8 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size)
        print_own_addr_type(cmd->own_addr_type);
        print_addr_type("Direct address type", cmd->direct_addr_type);
        print_addr("Direct address", cmd->direct_addr, cmd->direct_addr_type);
-
-       switch (cmd->channel_map) {
-       case 0x01:
-               str = "37";
-               break;
-       case 0x02:
-               str = "38";
-               break;
-       case 0x03:
-               str = "37, 38";
-               break;
-       case 0x04:
-               str = "39";
-               break;
-       case 0x05:
-               str = "37, 39";
-               break;
-       case 0x06:
-               str = "38, 39";
-               break;
-       case 0x07:
-               str = "37, 38, 39";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Channel map: %s (0x%2.2x)", str, cmd->channel_map);
-
-       switch (cmd->filter_policy) {
-       case 0x00:
-               str = "Allow Scan Request from Any, "
-                       "Allow Connect Request from Any";
-               break;
-       case 0x01:
-               str = "Allow Scan Request from White List Only, "
-                       "Allow Connect Request from Any";
-               break;
-       case 0x02:
-               str = "Allow Scan Request from Any, "
-                       "Allow Connect Request from White List Only";
-               break;
-       case 0x03:
-               str = "Allow Scan Request from White List Only, "
-                       "Allow Connect Request from White List Only";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+       print_adv_channel_map("Channel map", cmd->channel_map);
+       print_adv_filter_policy("Filter policy", cmd->filter_policy);
 }
 
 static void le_read_adv_tx_power_rsp(const void *data, uint8_t size)
@@ -6220,29 +6271,15 @@ static void le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
 static void le_set_adv_enable_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_le_set_adv_enable *cmd = data;
-       const char *str;
-
-       switch (cmd->enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
 
-       print_field("Advertising: %s (0x%2.2x)", str, cmd->enable);
+       print_enable("Advertising", cmd->enable);
 }
 
-static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
+static void print_scan_type(const char *label, uint8_t type)
 {
-       const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
        const char *str;
 
-       switch (cmd->type) {
+       switch (type) {
        case 0x00:
                str = "Passive";
                break;
@@ -6254,59 +6291,51 @@ static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
                break;
        }
 
-       print_field("Type: %s (0x%2.2x)", str, cmd->type);
+       print_field("%s: %s (0x%2.2x)", label, str, type);
+}
 
-       print_interval(cmd->interval);
-       print_window(cmd->window);
-       print_own_addr_type(cmd->own_addr_type);
+static void print_scan_filter_policy(uint8_t policy)
+{
+       const char *str;
 
-       switch (cmd->filter_policy) {
+       switch (policy) {
        case 0x00:
                str = "Accept all advertisement";
                break;
        case 0x01:
                str = "Ignore not in white list";
                break;
+       case 0x02:
+               str = "Accept all advertisement, inc. directed unresolved RPA";
+               break;
+       case 0x03:
+               str = "Ignore not in white list, exc. directed unresolved RPA";
+               break;
        default:
                str = "Reserved";
                break;
        }
 
-       print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+       print_field("Filter policy: %s (0x%2.2x)", str, policy);
 }
 
-static void le_set_scan_enable_cmd(const void *data, uint8_t size)
+static void le_set_scan_parameters_cmd(const void *data, uint8_t size)
 {
-       const struct bt_hci_cmd_le_set_scan_enable *cmd = data;
-       const char *str;
-
-       switch (cmd->enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
+       const struct bt_hci_cmd_le_set_scan_parameters *cmd = data;
 
-       print_field("Scanning: %s (0x%2.2x)", str, cmd->enable);
+       print_scan_type("Type", cmd->type);
+       print_interval(cmd->interval);
+       print_window(cmd->window);
+       print_own_addr_type(cmd->own_addr_type);
+       print_scan_filter_policy(cmd->filter_policy);
+}
 
-       switch (cmd->filter_dup) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
+static void le_set_scan_enable_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_scan_enable *cmd = data;
 
-       print_field("Filter duplicates: %s (0x%2.2x)", str, cmd->filter_dup);
+       print_enable("Scanning", cmd->enable);
+       print_enable("Filter duplicates", cmd->filter_dup);
 }
 
 static void le_create_conn_cmd(const void *data, uint8_t size)
@@ -6337,7 +6366,7 @@ static void le_create_conn_cmd(const void *data, uint8_t size)
 
        print_slot_125("Min connection interval", cmd->min_interval);
        print_slot_125("Max connection interval", cmd->max_interval);
-       print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+       print_conn_latency("Connection latency", cmd->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(cmd->supv_timeout) * 10,
                                        le16_to_cpu(cmd->supv_timeout));
@@ -6376,7 +6405,7 @@ static void le_conn_update_cmd(const void *data, uint8_t size)
        print_handle(cmd->handle);
        print_slot_125("Min connection interval", cmd->min_interval);
        print_slot_125("Max connection interval", cmd->max_interval);
-       print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+       print_conn_latency("Connection latency", cmd->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(cmd->supv_timeout) * 10,
                                        le16_to_cpu(cmd->supv_timeout));
@@ -6520,7 +6549,7 @@ static void le_conn_param_req_reply_cmd(const void *data, uint8_t size)
        print_handle(cmd->handle);
        print_slot_125("Min connection interval", cmd->min_interval);
        print_slot_125("Max connection interval", cmd->max_interval);
-       print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
+       print_conn_latency("Connection latency", cmd->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(cmd->supv_timeout) * 10,
                                        le16_to_cpu(cmd->supv_timeout));
@@ -6654,21 +6683,8 @@ static void le_read_local_resolv_addr_rsp(const void *data, uint8_t size)
 static void le_set_resolv_enable_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_le_set_resolv_enable *cmd = data;
-       const char *str;
-
-       switch (cmd->enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
 
-       print_field("Address resolution: %s (0x%2.2x)", str, cmd->enable);
+       print_enable("Address resolution", cmd->enable);
 }
 
 static void le_set_resolv_timeout_cmd(const void *data, uint8_t size)
@@ -6689,12 +6705,813 @@ static void le_read_max_data_length_rsp(const void *data, uint8_t size)
        print_field("Max RX time: %d", le16_to_cpu(rsp->max_rx_time));
 }
 
-struct opcode_data {
-       uint16_t opcode;
-       int bit;
+static void le_read_phy_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_read_phy *cmd = data;
+
+       print_handle(cmd->handle);
+}
+
+static void print_le_phy(const char *prefix, uint8_t phy)
+{
        const char *str;
-       void (*cmd_func) (const void *data, uint8_t size);
-       uint8_t cmd_size;
+
+       switch (phy) {
+       case 0x01:
+               str = "LE 1M";
+               break;
+       case 0x02:
+               str = "LE 2M";
+               break;
+       case 0x03:
+               str = "LE Coded";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s: %s (0x%2.2x)", prefix, str, phy);
+}
+
+static void le_read_phy_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_phy *rsp = data;
+
+       print_status(rsp->status);
+       print_handle(rsp->handle);
+       print_le_phy("TX PHY", rsp->tx_phy);
+       print_le_phy("RX PHY", rsp->rx_phy);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} le_phys[] = {
+       {  0, "LE 1M"   },
+       {  1, "LE 2M"   },
+       {  2, "LE Coded"},
+       { }
+};
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} le_phy_preference[] = {
+       {  0, "No TX PHY preference"    },
+       {  1, "No RX PHY preference"    },
+       { }
+};
+
+static void print_le_phys_preference(uint8_t all_phys, uint8_t tx_phys,
+                                                       uint8_t rx_phys)
+{
+       int i;
+       uint8_t mask = all_phys;
+
+       print_field("All PHYs preference: 0x%2.2x", all_phys);
+
+       for (i = 0; le_phy_preference[i].str; i++) {
+               if (all_phys & (((uint8_t) 1) << le_phy_preference[i].bit)) {
+                       print_field("  %s", le_phy_preference[i].str);
+                       mask &= ~(((uint64_t) 1) << le_phy_preference[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Reserved"
+                                                       " (0x%2.2x)", mask);
+
+       print_field("TX PHYs preference: 0x%2.2x", tx_phys);
+       mask = tx_phys;
+
+       for (i = 0; le_phys[i].str; i++) {
+               if (tx_phys & (((uint8_t) 1) << le_phys[i].bit)) {
+                       print_field("  %s", le_phys[i].str);
+                       mask &= ~(((uint64_t) 1) << le_phys[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Reserved"
+                                                       " (0x%2.2x)", mask);
+
+       print_field("RX PHYs preference: 0x%2.2x", rx_phys);
+       mask = rx_phys;
+
+       for (i = 0; le_phys[i].str; i++) {
+               if (rx_phys & (((uint8_t) 1) << le_phys[i].bit)) {
+                       print_field("  %s", le_phys[i].str);
+                       mask &= ~(((uint64_t) 1) << le_phys[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_OPTIONS_BIT, "  Reserved"
+                                                       " (0x%2.2x)", mask);
+}
+
+static void le_set_default_phy_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_default_phy *cmd = data;
+
+       print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys);
+}
+
+static void le_set_phy_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_phy *cmd = data;
+       const char *str;
+
+       print_handle(cmd->handle);
+       print_le_phys_preference(cmd->all_phys, cmd->tx_phys, cmd->rx_phys);
+       switch (le16_to_cpu(cmd->phy_opts)) {
+       case 0x0001:
+               str = "S2 coding";
+               break;
+       case 0x0002:
+               str = "S8 coding";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("PHY options preference: %s (0x%4.4x)", str, cmd->phy_opts);
+}
+
+static void le_enhanced_receiver_test_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_enhanced_receiver_test *cmd = data;
+       const char *str;
+
+       print_field("RX channel frequency: %d MHz (0x%2.2x)",
+                               (cmd->rx_channel * 2) + 2402, cmd->rx_channel);
+       print_le_phy("PHY", cmd->phy);
+
+       switch (cmd->modulation_index) {
+       case 0x00:
+               str = "Standard";
+               break;
+       case 0x01:
+               str = "Stable";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Modulation index: %s (0x%2.2x)", str,
+                                                       cmd->modulation_index);
+}
+
+static void le_enhanced_transmitter_test_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_enhanced_transmitter_test *cmd = data;
+       const char *str;
+
+       print_field("TX channel frequency: %d MHz (0x%2.2x)",
+                               (cmd->tx_channel * 2) + 2402, cmd->tx_channel);
+       print_field("Test data length: %d bytes", cmd->data_len);
+       print_field("Packet payload: 0x%2.2x", cmd->payload);
+
+       switch (cmd->phy) {
+       case 0x01:
+               str = "LE 1M";
+               break;
+       case 0x02:
+               str = "LE 2M";
+               break;
+       case 0x03:
+               str = "LE Coded with S=8";
+               break;
+       case 0x04:
+               str = "LE Coded with S=2";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("PHY: %s (0x%2.2x)", str, cmd->phy);
+}
+
+static void le_set_adv_set_rand_addr(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_adv_set_rand_addr *cmd = data;
+
+       print_field("Advertising handle: 0x%2.2x", cmd->handle);
+       print_addr("Advertising random address", cmd->bdaddr, 0x00);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} ext_adv_properties_table[] = {
+       {  0, "Connectable"             },
+       {  1, "Scannable"               },
+       {  2, "Directed"        },
+       {  3, "High Duty Cycle Directed Connectable"    },
+       {  4, "Use legacy advertising PDUs"     },
+       {  5, "Anonymous advertising"   },
+       {  6, "Include TxPower"         },
+       { }
+};
+
+static const char *get_adv_pdu_desc(uint16_t flags)
+{
+       const char *str;
+
+       switch (flags) {
+       case 0x10:
+               str = "ADV_NONCONN_IND";
+               break;
+       case 0x12:
+               str = "ADV_SCAN_IND";
+               break;
+       case 0x13:
+               str = "ADV_IND";
+               break;
+       case 0x15:
+               str = "ADV_DIRECT_IND (low duty cycle)";
+               break;
+       case 0x1d:
+               str = "ADV_DIRECT_IND (high duty cycle)";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       return str;
+}
+
+static void print_ext_adv_properties(uint16_t flags)
+{
+       uint16_t mask = flags;
+       const char *property;
+       int i;
+
+       print_field("Properties: 0x%4.4x", flags);
+
+       for (i = 0; ext_adv_properties_table[i].str; i++) {
+               if (flags & (1 << ext_adv_properties_table[i].bit)) {
+                       property = ext_adv_properties_table[i].str;
+
+                       if (ext_adv_properties_table[i].bit == 4) {
+                               print_field("  %s: %s", property,
+                                               get_adv_pdu_desc(flags));
+                       } else {
+                               print_field("  %s", property);
+                       }
+                       mask &= ~(1 << ext_adv_properties_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_ADV_FLAG,
+                               "  Unknown advertising properties (0x%4.4x)",
+                                                                       mask);
+}
+
+static void print_ext_slot_625(const char *label, const uint8_t value[3])
+{
+       uint32_t value_cpu = value[0];
+
+       value_cpu |= value[1] << 8;
+       value_cpu |= value[2] << 16;
+
+       print_field("%s: %.3f msec (0x%4.4x)", label,
+                                               value_cpu * 0.625, value_cpu);
+}
+
+static void le_set_ext_adv_params_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_adv_params *cmd = data;
+       const char *str;
+
+       print_field("Handle: 0x%2.2x", cmd->handle);
+       print_ext_adv_properties(le16_to_cpu(cmd->evt_properties));
+
+       print_ext_slot_625("Min advertising interval", cmd->min_interval);
+       print_ext_slot_625("Max advertising interval", cmd->max_interval);
+       print_adv_channel_map("Channel map", cmd->channel_map);
+       print_own_addr_type(cmd->own_addr_type);
+       print_peer_addr_type("Peer address type", cmd->peer_addr_type);
+       print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+       print_adv_filter_policy("Filter policy", cmd->filter_policy);
+       print_power_level(cmd->tx_power, NULL);
+
+       switch (cmd->primary_phy) {
+       case 0x01:
+               str = "LE 1M";
+               break;
+       case 0x03:
+               str = "LE Coded";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Primary PHY: %s (0x%2.2x)", str, cmd->primary_phy);
+       print_field("Secondary max skip: 0x%2.2x", cmd->secondary_max_skip);
+       print_le_phy("Secondary PHY", cmd->secondary_phy);
+       print_field("SID: 0x%2.2x", cmd->sid);
+       print_enable("Scan request notifications", cmd->notif_enable);
+}
+
+static void le_set_ext_adv_params_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_set_ext_adv_params *rsp = data;
+
+       print_status(rsp->status);
+       print_power_level(rsp->tx_power, "selected");
+}
+
+static void le_set_ext_adv_data_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_adv_data *cmd = data;
+       const char *str;
+
+       print_field("Handle: 0x%2.2x", cmd->handle);
+
+       switch (cmd->operation) {
+       case 0x00:
+               str = "Immediate fragment";
+               break;
+       case 0x01:
+               str = "First fragment";
+               break;
+       case 0x02:
+               str = "Last fragment";
+               break;
+       case 0x03:
+               str = "Complete extended advertising data";
+               break;
+       case 0x04:
+               str = "Unchanged data";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+
+       switch (cmd->fragment_preference) {
+       case 0x00:
+               str = "Fragment all";
+               break;
+       case 0x01:
+               str = "Minimize fragmentation";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Fragment preference: %s (0x%2.2x)", str,
+                                               cmd->fragment_preference);
+       print_field("Data length: 0x%2.2x", cmd->data_len);
+       packet_print_ad(cmd->data, size - sizeof(*cmd));
+}
+
+static void le_set_ext_scan_rsp_data_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_scan_rsp_data *cmd = data;
+       const char *str;
+
+       print_field("Handle: 0x%2.2x", cmd->handle);
+
+       switch (cmd->operation) {
+       case 0x00:
+               str = "Immediate fragment";
+               break;
+       case 0x01:
+               str = "First fragment";
+               break;
+       case 0x02:
+               str = "Last fragment";
+               break;
+       case 0x03:
+               str = "Complete scan response data";
+               break;
+       case 0x04:
+               str = "Unchanged data";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+
+       switch (cmd->fragment_preference) {
+       case 0x00:
+               str = "Fragment all";
+               break;
+       case 0x01:
+               str = "Minimize fragmentation";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Fragment preference: %s (0x%2.2x)", str,
+                                               cmd->fragment_preference);
+       print_field("Data length: 0x%2.2x", cmd->data_len);
+       packet_print_ad(cmd->data, size - sizeof(*cmd));
+}
+
+static void le_set_ext_adv_enable_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_adv_enable *cmd = data;
+       const struct bt_hci_cmd_ext_adv_set *adv_set;
+       int i;
+
+       print_enable("Extended advertising", cmd->enable);
+
+       if (cmd->num_of_sets == 0)
+               print_field("Number of sets: Disable all sets (0x%2.2x)",
+                                                       cmd->num_of_sets);
+       else if (cmd->num_of_sets > 0x3f)
+               print_field("Number of sets: Reserved (0x%2.2x)",
+                                                       cmd->num_of_sets);
+       else
+               print_field("Number of sets: %u (0x%2.2x)", cmd->num_of_sets,
+                                                       cmd->num_of_sets);
+
+       for (i = 0; i < cmd->num_of_sets; ++i) {
+               adv_set = data + 2 + i * sizeof(struct bt_hci_cmd_ext_adv_set);
+               print_field("Entry %d", i);
+               print_field("  Handle: 0x%2.2x", adv_set->handle);
+               print_field("  Duration: %d ms (0x%2.2x)",
+                               adv_set->duration * 10, adv_set->duration);
+               print_field("  Max ext adv events: %d", adv_set->max_events);
+       }
+}
+
+static void le_read_max_adv_data_len_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_max_adv_data_len *rsp = data;
+
+       print_status(rsp->status);
+       print_field("Max length: %d", rsp->max_len);
+}
+
+static void le_read_num_supported_adv_sets_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_num_supported_adv_sets *rsp = data;
+
+       print_status(rsp->status);
+       print_field("Num supported adv sets: %d", rsp->num_of_sets);
+}
+
+static void le_remove_adv_set_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_remove_adv_set *cmd = data;
+
+       print_handle(cmd->handle);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} periodic_adv_properties_table[] = {
+       {  6, "Include TxPower"         },
+       { }
+};
+
+static void print_periodic_adv_properties(uint16_t flags)
+{
+       uint16_t mask = flags;
+       int i;
+
+       print_field("Properties: 0x%4.4x", flags);
+
+       for (i = 0; periodic_adv_properties_table[i].str; i++) {
+               if (flags & (1 << periodic_adv_properties_table[i].bit)) {
+                       print_field("  %s",
+                                       periodic_adv_properties_table[i].str);
+                       mask &= ~(1 << periodic_adv_properties_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_ADV_FLAG,
+                               "  Unknown advertising properties (0x%4.4x)",
+                                                                       mask);
+}
+
+static void le_set_periodic_adv_params_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_periodic_adv_params *cmd = data;
+
+       print_handle(cmd->handle);
+       print_slot_125("Min interval", cmd->min_interval);
+       print_slot_125("Max interval", cmd->max_interval);
+       print_periodic_adv_properties(cmd->properties);
+}
+
+static void le_set_periodic_adv_data_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_periodic_adv_data *cmd = data;
+       const char *str;
+
+       print_handle(cmd->handle);
+
+       switch (cmd->operation) {
+       case 0x00:
+               str = "Immediate fragment";
+               break;
+       case 0x01:
+               str = "First fragment";
+               break;
+       case 0x02:
+               str = "Last fragment";
+               break;
+       case 0x03:
+               str = "Complete ext advertising data";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Operation: %s (0x%2.2x)", str, cmd->operation);
+       print_field("Data length: 0x%2.2x", cmd->data_len);
+       print_eir(cmd->data, cmd->data_len, true);
+}
+
+static void le_set_periodic_adv_enable_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_periodic_adv_enable *cmd = data;
+
+       print_enable("Periodic advertising", cmd->enable);
+       print_handle(cmd->handle);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} ext_scan_phys_table[] = {
+       {  0, "LE 1M"           },
+       {  2, "LE Coded"                },
+       { }
+};
+
+static void print_ext_scan_phys(const void *data, uint8_t flags)
+{
+       const struct bt_hci_le_scan_phy *scan_phy;
+       uint8_t mask = flags;
+       int bits_set = 0;
+       int i;
+
+       print_field("PHYs: 0x%2.2x", flags);
+
+       for (i = 0; ext_scan_phys_table[i].str; i++) {
+               if (flags & (1 << ext_scan_phys_table[i].bit)) {
+                       scan_phy = data + bits_set * sizeof(*scan_phy);
+                       mask &= ~(1 << ext_scan_phys_table[i].bit);
+
+                       print_field("Entry %d: %s", bits_set,
+                                               ext_scan_phys_table[i].str);
+                       print_scan_type("  Type", scan_phy->type);
+                       print_slot_625("  Interval", scan_phy->interval);
+                       print_slot_625("  Window", scan_phy->window);
+
+                       ++bits_set;
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_ADV_FLAG, "  Unknown scanning PHYs"
+                                                       " (0x%2.2x)", mask);
+}
+
+static void le_set_ext_scan_params_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_scan_params *cmd = data;
+
+       print_own_addr_type(cmd->own_addr_type);
+       print_scan_filter_policy(cmd->filter_policy);
+       print_ext_scan_phys(cmd->data, cmd->num_phys);
+}
+
+static void le_set_ext_scan_enable_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_ext_scan_enable *cmd = data;
+
+       print_enable("Extended scan", cmd->enable);
+       print_enable("Filter duplicates", cmd->filter_dup);
+
+       print_field("Duration: %d msec (0x%4.4x)",
+                                               le16_to_cpu(cmd->duration) * 10,
+                                               le16_to_cpu(cmd->duration));
+       print_field("Period: %.2f sec (0x%4.4x)",
+                                               le16_to_cpu(cmd->period) * 1.28,
+                                               le16_to_cpu(cmd->period));
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} ext_conn_phys_table[] = {
+       {  0, "LE 1M"           },
+       {  1, "LE 2M"           },
+       {  2, "LE Coded"                },
+       { }
+};
+
+static void print_ext_conn_phys(const void *data, uint8_t flags)
+{
+       const struct bt_hci_le_ext_create_conn *entry;
+       uint8_t mask = flags;
+       int bits_set = 0;
+       int i;
+
+       print_field("Initiating PHYs: 0x%2.2x", flags);
+
+       for (i = 0; ext_conn_phys_table[i].str; i++) {
+               if (flags & (1 << ext_conn_phys_table[i].bit)) {
+                       entry = data + bits_set * sizeof(*entry);
+                       mask &= ~(1 << ext_conn_phys_table[i].bit);
+
+                       print_field("Entry %d: %s", bits_set,
+                                               ext_conn_phys_table[i].str);
+                       print_slot_625("  Scan interval", entry->scan_interval);
+                       print_slot_625("  Scan window", entry->scan_window);
+                       print_slot_125("  Min connection interval",
+                                                       entry->min_interval);
+                       print_slot_125("  Max connection interval",
+                                                       entry->max_interval);
+                       print_conn_latency("  Connection latency",
+                                                               entry->latency);
+                       print_field("  Supervision timeout: %d msec (0x%4.4x)",
+                                       le16_to_cpu(entry->supv_timeout) * 10,
+                                       le16_to_cpu(entry->supv_timeout));
+                       print_slot_625("  Min connection length",
+                                                       entry->min_length);
+                       print_slot_625("  Max connection length",
+                                                       entry->max_length);
+
+                       ++bits_set;
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_ADV_FLAG, "  Unknown scanning PHYs"
+                                                       " (0x%2.2x)", mask);
+}
+
+static void le_ext_create_conn_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_ext_create_conn *cmd = data;
+       const char *str;
+
+       switch (cmd->filter_policy) {
+       case 0x00:
+               str = "White list is not used";
+               break;
+       case 0x01:
+               str = "White list is used";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+
+       print_own_addr_type(cmd->own_addr_type);
+       print_peer_addr_type("Peer address type", cmd->peer_addr_type);
+       print_addr("Peer address", cmd->peer_addr, cmd->peer_addr_type);
+       print_ext_conn_phys(cmd->data, cmd->phys);
+}
+
+static void le_periodic_adv_create_sync_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_periodic_adv_create_sync *cmd = data;
+       const char *str;
+
+       switch (cmd->filter_policy) {
+       case 0x00:
+               str = "Use specified advertising parameters";
+               break;
+       case 0x01:
+               str = "Use Periodic Advertiser List";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Filter policy: %s (0x%2.2x)", str, cmd->filter_policy);
+       print_field("SID: 0x%2.2x", cmd->sid);
+       print_addr_type("Adv address type", cmd->addr_type);
+       print_addr("Adv address", cmd->addr, cmd->addr_type);
+       print_field("Skip: 0x%4.4x", cmd->skip);
+       print_field("Sync timeout: %d msec (0x%4.4x)",
+                                       le16_to_cpu(cmd->sync_timeout) * 10,
+                                       le16_to_cpu(cmd->sync_timeout));
+       print_field("Unused: 0x%2.2x", cmd->unused);
+}
+
+static void le_periodic_adv_term_sync_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_periodic_adv_term_sync *cmd = data;
+
+       print_field("Sync handle: 0x%4.4x", cmd->sync_handle);
+}
+
+static void le_add_dev_periodic_adv_list_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_add_dev_periodic_adv_list *cmd = data;
+
+       print_addr_type("Adv address type", cmd->addr_type);
+       print_addr("Adv address", cmd->addr, cmd->addr_type);
+       print_field("SID: 0x%2.2x", cmd->sid);
+}
+
+static void le_remove_dev_periodic_adv_list_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_remove_dev_periodic_adv_list *cmd = data;
+
+       print_addr_type("Adv address type", cmd->addr_type);
+       print_addr("Adv address", cmd->addr, cmd->addr_type);
+       print_field("SID: 0x%2.2x", cmd->sid);
+}
+
+static void le_read_periodic_adv_list_size_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_dev_periodic_adv_list_size *rsp = data;
+
+       print_status(rsp->status);
+       print_field("List size: 0x%2.2x", rsp->list_size);
+}
+
+static void le_read_tx_power_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_tx_power *rsp = data;
+
+       print_status(rsp->status);
+       print_field("Min Tx power: %d dBm", rsp->min_tx_power);
+       print_field("Max Tx power: %d dBm", rsp->max_tx_power);
+}
+
+static void le_read_rf_path_comp_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_le_read_rf_path_comp *rsp = data;
+
+       print_status(rsp->status);
+       print_field("RF Tx Path Compensation Value: 0x%4.4x",
+                                                       rsp->rf_tx_path_comp);
+       print_field("RF Rx Path Compensation Value: 0x%4.4x",
+                                                       rsp->rf_rx_path_comp);
+}
+
+static void le_write_rf_path_comp_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_write_rf_path_comp *cmd = data;
+
+       print_field("RF Tx Path Compensation Value: 0x%4.4x",
+                                                       cmd->rf_tx_path_comp);
+       print_field("RF Rx Path Compensation Value: 0x%4.4x",
+                                                       cmd->rf_rx_path_comp);
+}
+
+static void le_set_priv_mode_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_priv_mode *cmd = data;
+       const char *str;
+
+       print_addr_type("Peer Identity address type", cmd->peer_id_addr_type);
+       print_addr("Peer Identity address", cmd->peer_id_addr,
+                                                       cmd->peer_id_addr_type);
+
+       switch (cmd->priv_mode) {
+       case 0x00:
+               str = "Use Network Privacy";
+               break;
+       case 0x01:
+               str = "Use Device Privacy";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Privacy Mode: %s (0x%2.2x)", str, cmd->priv_mode);
+}
+
+struct opcode_data {
+       uint16_t opcode;
+       int bit;
+       const char *str;
+       void (*cmd_func) (const void *data, uint8_t size);
+       uint8_t cmd_size;
        bool cmd_fixed;
        void (*rsp_func) (const void *data, uint8_t size);
        uint8_t rsp_size;
@@ -7384,6 +8201,98 @@ static const struct opcode_data opcode_table[] = {
        { 0x202f, 283, "LE Read Maximum Data Length",
                                null_cmd, 0, true,
                                le_read_max_data_length_rsp, 9, true },
+       { 0x2030, 284, "LE Read PHY",
+                               le_read_phy_cmd, 2, true,
+                               le_read_phy_rsp, 5, true},
+       { 0x2031, 285, "LE Set Default PHY",
+                               le_set_default_phy_cmd, 3, true,
+                               status_rsp, 1, true },
+       { 0x2032, 286, "LE Set PHY",
+                               le_set_phy_cmd, 7, true},
+       { 0x2033, 287, "LE Enhanced Receiver Test",
+                               le_enhanced_receiver_test_cmd, 3, true,
+                               status_rsp, 1, true },
+       { 0x2034, 288, "LE Enhanced Transmitter Test",
+                               le_enhanced_transmitter_test_cmd, 4, true,
+                               status_rsp, 1, true },
+       { 0x2035, 289, "LE Set Advertising Set Random Address",
+                               le_set_adv_set_rand_addr, 7, true,
+                               status_rsp, 1, true },
+       { 0x2036, 290, "LE Set Extended Advertising Parameters",
+                               le_set_ext_adv_params_cmd, 25, true,
+                               le_set_ext_adv_params_rsp, 2, true },
+       { 0x2037, 291, "LE Set Extended Advertising Data",
+                               le_set_ext_adv_data_cmd, 4, false,
+                               status_rsp, 1, true },
+       { 0x2038, 292, "LE Set Extended Scan Response Data",
+                               le_set_ext_scan_rsp_data_cmd, 4, false,
+                               status_rsp, 1, true },
+       { 0x2039, 293, "LE Set Extended Advertising Enable",
+                               le_set_ext_adv_enable_cmd, 2, false,
+                               status_rsp, 1, true },
+       { 0x203a, 294, "LE Read Maximum Advertising Data Length",
+                               null_cmd, 0, true,
+                               le_read_max_adv_data_len_rsp, 3, true },
+       { 0x203b, 295, "LE Read Number of Supported Advertising Sets",
+                               null_cmd, 0, true,
+                               le_read_num_supported_adv_sets_rsp, 2, true },
+       { 0x203c, 296, "LE Remove Advertising Set",
+                               le_remove_adv_set_cmd, 1, true,
+                               status_rsp, 1, true },
+       { 0x203d, 297, "LE Clear Advertising Sets",
+                               null_cmd, 0, true,
+                               status_rsp, 1, true },
+       { 0x203e, 298, "LE Set Periodic Advertising Parameters",
+                               le_set_periodic_adv_params_cmd, 7, true,
+                               status_rsp, 1, true },
+       { 0x203f, 299, "LE Set Periodic Advertising Data",
+                               le_set_periodic_adv_data_cmd, 3, false,
+                               status_rsp, 1, true },
+       { 0x2040, 300, "LE Set Periodic Advertising Enable",
+                               le_set_periodic_adv_enable_cmd, 2, true,
+                               status_rsp, 1, true },
+       { 0x2041, 301, "LE Set Extended Scan Parameters",
+                               le_set_ext_scan_params_cmd, 3, false,
+                               status_rsp, 1, true },
+       { 0x2042, 302, "LE Set Extended Scan Enable",
+                               le_set_ext_scan_enable_cmd, 6, true,
+                               status_rsp, 1, true },
+       { 0x2043, 303, "LE Extended Create Connection",
+                               le_ext_create_conn_cmd, 10, false,
+                               status_rsp, 1, true },
+       { 0x2044, 304, "LE Periodic Advertising Create Sync",
+                               le_periodic_adv_create_sync_cmd, 14, true,
+                               status_rsp, 1, true },
+       { 0x2045, 305, "LE Periodic Advertising Create Sync Cancel",
+                               null_cmd, 0, true,
+                               status_rsp, 1, true },
+       { 0x2046, 306, "LE Periodic Advertising Terminate Sync",
+                               le_periodic_adv_term_sync_cmd, 2, true,
+                               status_rsp, 1, true },
+       { 0x2047, 307, "LE Add Device To Periodic Advertiser List",
+                               le_add_dev_periodic_adv_list_cmd, 8, true,
+                               status_rsp, 1, true },
+       { 0x2048, 308, "LE Remove Device From Periodic Advertiser List",
+                               le_remove_dev_periodic_adv_list_cmd, 8, true,
+                               status_rsp, 1, true },
+       { 0x2049, 309, "LE Clear Periodic Advertiser List",
+                               null_cmd, 0, true,
+                               status_rsp, 1, true },
+       { 0x204a, 310, "LE Read Periodic Advertiser List Size",
+                               null_cmd, 0, true,
+                               le_read_periodic_adv_list_size_rsp, 2, true },
+       { 0x204b, 311, "LE Read Transmit Power",
+                               null_cmd, 0, true,
+                               le_read_tx_power_rsp, 3, true },
+       { 0x204c, 312, "LE Read RF Path Compensation",
+                               null_cmd, 0, true,
+                               le_read_rf_path_comp_rsp, 5, true },
+       { 0x204d, 313, "LE Write RF Path Compensation",
+                               le_write_rf_path_comp_cmd, 4, true,
+                               status_rsp, 1, true },
+       { 0x204e, 314, "LE Set Privacy Mode",
+                               le_set_priv_mode_cmd, 8, true,
+                               status_rsp, 1, true },
        { }
 };
 
@@ -7414,8 +8323,8 @@ static const char *current_vendor_str(void)
        case 15:
                return "Broadcom";
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       default:
-               return "Unknown";
+        default:
+                return "Unknown";
 #endif
        }
 
@@ -7437,8 +8346,8 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
        case 15:
                return broadcom_vendor_ocf(ocf);
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       default:
-               return broadcom_vendor_ocf(ocf);
+        default:
+                return broadcom_vendor_ocf(ocf);
 #endif
        }
 
@@ -7460,8 +8369,8 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)
        case 15:
                return broadcom_vendor_evt(evt);
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       default:
-               return broadcom_vendor_evt(evt);
+        default:
+                return broadcom_vendor_evt(evt);
 #endif
        }
 
@@ -7499,7 +8408,7 @@ static void conn_complete_evt(const void *data, uint8_t size)
        print_handle(evt->handle);
        print_bdaddr(evt->bdaddr);
        print_link_type(evt->link_type);
-       print_encr_mode(evt->encr_mode);
+       print_enable("Encryption", evt->encr_mode);
 
        if (evt->status == 0x00)
                assign_handle(le16_to_cpu(evt->handle), 0x00);
@@ -8224,7 +9133,7 @@ static void short_range_mode_change_evt(const void *data, uint8_t size)
 
        print_status(evt->status);
        print_phy_handle(evt->phy_handle);
-       print_short_range_mode(evt->mode);
+       print_enable("Short range mode", evt->mode);
 }
 
 static void amp_status_change_evt(const void *data, uint8_t size)
@@ -8341,7 +9250,7 @@ static void le_conn_complete_evt(const void *data, uint8_t size)
        print_peer_addr_type("Peer address type", evt->peer_addr_type);
        print_addr("Peer address", evt->peer_addr, evt->peer_addr_type);
        print_slot_125("Connection interval", evt->interval);
-       print_slot_125("Connection latency", evt->latency);
+       print_conn_latency("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(evt->supv_timeout) * 10,
                                        le16_to_cpu(evt->supv_timeout));
@@ -8360,7 +9269,7 @@ static void le_adv_report_evt(const void *data, uint8_t size)
        print_num_reports(evt->num_reports);
 
 report:
-       print_adv_event_type(evt->event_type);
+       print_adv_event_type("Event type", evt->event_type);
        print_peer_addr_type("Address type", evt->addr_type);
        print_addr("Address", evt->addr, evt->addr_type);
        print_field("Data length: %d", evt->data_len);
@@ -8386,7 +9295,7 @@ static void le_conn_update_complete_evt(const void *data, uint8_t size)
        print_status(evt->status);
        print_handle(evt->handle);
        print_slot_125("Connection interval", evt->interval);
-       print_slot_125("Connection latency", evt->latency);
+       print_conn_latency("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(evt->supv_timeout) * 10,
                                        le16_to_cpu(evt->supv_timeout));
@@ -8417,7 +9326,7 @@ static void le_conn_param_request_evt(const void *data, uint8_t size)
        print_handle(evt->handle);
        print_slot_125("Min connection interval", evt->min_interval);
        print_slot_125("Max connection interval", evt->max_interval);
-       print_field("Connection latency: 0x%4.4x", le16_to_cpu(evt->latency));
+       print_conn_latency("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(evt->supv_timeout) * 10,
                                        le16_to_cpu(evt->supv_timeout));
@@ -8462,7 +9371,7 @@ static void le_enhanced_conn_complete_evt(const void *data, uint8_t size)
        print_addr("Local resolvable private address", evt->local_rpa, 0x01);
        print_addr("Peer resolvable private address", evt->peer_rpa, 0x01);
        print_slot_125("Connection interval", evt->interval);
-       print_slot_125("Connection latency", evt->latency);
+       print_conn_latency("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
                                        le16_to_cpu(evt->supv_timeout) * 10,
                                        le16_to_cpu(evt->supv_timeout));
@@ -8478,7 +9387,7 @@ static void le_direct_adv_report_evt(const void *data, uint8_t size)
 
        print_num_reports(evt->num_reports);
 
-       print_adv_event_type(evt->event_type);
+       print_adv_event_type("Event type", evt->event_type);
        print_peer_addr_type("Address type", evt->addr_type);
        print_addr("Address", evt->addr, evt->addr_type);
        print_addr_type("Direct address type", evt->direct_addr_type);
@@ -8489,6 +9398,236 @@ static void le_direct_adv_report_evt(const void *data, uint8_t size)
                packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
 }
 
+static void le_phy_update_complete_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_le_phy_update_complete *evt = data;
+
+       print_status(evt->status);
+       print_handle(evt->handle);
+       print_le_phy("TX PHY", evt->tx_phy);
+       print_le_phy("RX PHY", evt->rx_phy);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} ext_adv_report_evt_type[] = {
+       {  0, "Connectable"             },
+       {  1, "Scannable"               },
+       {  2, "Directed"        },
+       {  3, "Scan response"   },
+       {  4, "Use legacy advertising PDUs"     },
+       { }
+};
+
+static void print_ext_adv_report_evt_type(const char *indent, uint16_t flags)
+{
+       uint16_t mask = flags;
+       uint16_t props = flags;
+       uint8_t data_status;
+       const char *str;
+       int i;
+
+       print_field("%sEvent type: 0x%4.4x", indent, flags);
+
+       props &= 0x1f;
+       print_field("%s  Props: 0x%4.4x", indent, props);
+       for (i = 0; ext_adv_report_evt_type[i].str; i++) {
+               if (flags & (1 << ext_adv_report_evt_type[i].bit)) {
+                       print_field("%s    %s", indent,
+                                               ext_adv_report_evt_type[i].str);
+                       mask &= ~(1 << ext_adv_report_evt_type[i].bit);
+               }
+       }
+
+       data_status = (flags >> 5) & 3;
+       mask &= ~(data_status << 5);
+
+       switch (data_status) {
+       case 0x00:
+               str = "Complete";
+               break;
+       case 0x01:
+               str = "Incomplete, more data to come";
+               break;
+       case 0x02:
+               str = "Incomplete, data truncated, no more to come";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s  Data status: %s", indent, str);
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_ADV_FLAG,
+                               "%s  Reserved (0x%4.4x)", indent, mask);
+}
+
+static void print_legacy_adv_report_pdu(uint16_t flags)
+{
+       const char *str;
+
+       if (!(flags & (1 << 4)))
+               return;
+
+       switch (flags) {
+       case 0x10:
+               str = "ADV_NONCONN_IND";
+               break;
+       case 0x12:
+               str = "ADV_SCAN_IND";
+               break;
+       case 0x13:
+               str = "ADV_IND";
+               break;
+       case 0x15:
+               str = "ADV_DIRECT_IND";
+               break;
+       case 0x1a:
+               str = "SCAN_RSP to an ADV_IND";
+               break;
+       case 0x1b:
+               str = "SCAN_RSP to an ADV_SCAN_IND";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  Legacy PDU Type: %s (0x%4.4x)", str, flags);
+}
+
+static void le_ext_adv_report_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_le_ext_adv_report *evt = data;
+       const struct bt_hci_le_ext_adv_report *report;
+       const char *str;
+       int i;
+
+       print_num_reports(evt->num_reports);
+
+       data += sizeof(evt->num_reports);
+
+       for (i = 0; i < evt->num_reports; ++i) {
+               report = data;
+               print_field("Entry %d", i);
+               print_ext_adv_report_evt_type("  ", report->event_type);
+               print_legacy_adv_report_pdu(report->event_type);
+               print_peer_addr_type("  Address type", report->addr_type);
+               print_addr("  Address", report->addr, report->addr_type);
+
+               switch (report->primary_phy) {
+               case 0x01:
+                       str = "LE 1M";
+                       break;
+               case 0x03:
+                       str = "LE Coded";
+                       break;
+               default:
+                       str = "Reserved";
+                       break;
+               }
+
+               print_field("  Primary PHY: %s", str);
+
+               switch (report->secondary_phy) {
+               case 0x00:
+                       str = "No packets";
+                       break;
+               case 0x01:
+                       str = "LE 1M";
+                       break;
+               case 0x02:
+                       str = "LE 2M";
+                       break;
+               case 0x03:
+                       str = "LE Coded";
+                       break;
+               default:
+                       str = "Reserved";
+                       break;
+               }
+
+               print_field("  Secondary PHY: %s", str);
+
+               if (report->sid == 0xff)
+                       print_field("  SID: no ADI field (0x%2.2x)",
+                                                               report->sid);
+               else if (report->sid > 0x0f)
+                       print_field("  SID: Reserved (0x%2.2x)", report->sid);
+               else
+                       print_field("  SID: 0x%2.2x", report->sid);
+
+               print_field("  TX power: %d dBm", report->tx_power);
+
+               if (report->rssi == 127)
+                       print_field("  RSSI: not available (0x%2.2x)",
+                                                       (uint8_t) report->rssi);
+               else if (report->rssi >= -127 && report->rssi <= 20)
+                       print_field("  RSSI: %d dBm (0x%2.2x)",
+                                       report->rssi, (uint8_t) report->rssi);
+               else
+                       print_field("  RSSI: reserved (0x%2.2x)",
+                                                       (uint8_t) report->rssi);
+
+               print_slot_125("  Periodic advertising invteral",
+                                                       report->interval);
+               print_peer_addr_type("  Direct address type",
+                                               report->direct_addr_type);
+               print_addr("  Direct address", report->direct_addr,
+                                               report->direct_addr_type);
+               print_field("  Data length: 0x%2.2x", report->data_len);
+               data += sizeof(struct bt_hci_le_ext_adv_report);
+               packet_hexdump(data, report->data_len);
+               data += report->data_len;
+       }
+}
+
+static void le_adv_set_term_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_le_adv_set_term *evt = data;
+
+       print_status(evt->status);
+       print_field("Handle: %d", evt->handle);
+       print_field("Connection handle: %d", evt->conn_handle);
+       print_field("Number of completed extended advertising events: %d",
+                       evt->num_evts);
+}
+
+static void le_scan_req_received_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_le_scan_req_received *evt = data;
+
+       print_field("Handle: %d", evt->handle);
+       print_peer_addr_type("Scanner address type", evt->scanner_addr_type);
+       print_addr("Scanner address", evt->scanner_addr,
+                                                       evt->scanner_addr_type);
+}
+
+static void le_chan_select_alg_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_le_chan_select_alg *evt = data;
+       const char *str;
+
+       print_handle(evt->handle);
+
+       switch (evt->algorithm) {
+       case 0x00:
+               str = "#1";
+               break;
+       case 0x01:
+               str = "#2";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Algorithm: %s (0x%2.2x)", str, evt->algorithm);
+}
+
 struct subevent_data {
        uint8_t subevent;
        const char *str;
@@ -8555,6 +9694,20 @@ static const struct subevent_data le_meta_event_table[] = {
                                le_enhanced_conn_complete_evt, 30, true },
        { 0x0b, "LE Direct Advertising Report",
                                le_direct_adv_report_evt, 1, false },
+       { 0x0c, "LE PHY Update Complete",
+                               le_phy_update_complete_evt, 5, true},
+       { 0x0d, "LE Extended Advertising Report",
+                               le_ext_adv_report_evt, 1, false},
+       { 0x0e, "LE Periodic Advertising Sync Established" },
+       { 0x0f, "LE Periodic Advertising Report" },
+       { 0x10, "LE Periodic Advertising Sync Lost" },
+       { 0x11, "LE Scan Timeout" },
+       { 0x12, "LE Advertising Set Terminated",
+                               le_adv_set_term_evt, 5, true},
+       { 0x13, "LE Scan Request Received",
+                               le_scan_req_received_evt, 8, true},
+       { 0x14, "LE Channel Selection Algorithm",
+                               le_chan_select_alg_evt, 3, true},
        { }
 };
 
@@ -8773,6 +9926,7 @@ static const struct event_data event_table[] = {
                                inquiry_response_notify_evt, 4, true },
        { 0x57, "Authenticated Payload Timeout Expired",
                                auth_payload_timeout_expired_evt, 2, true },
+       { 0x58, "SAM Status Change" },
        { 0xfe, "Testing" },
        { 0xff, "Vendor", vendor_evt, 0, false },
        { }
@@ -8920,6 +10074,8 @@ void packet_hci_command(struct timeval *tv, struct ucred *cred, uint16_t index,
        char extra_str[25], vendor_str[150];
        int i;
 
+       index_list[index].frame++;
+
        if (size < HCI_COMMAND_HDR_SIZE) {
                sprintf(extra_str, "(len %d)", size);
                print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
@@ -9021,6 +10177,8 @@ void packet_hci_event(struct timeval *tv, struct ucred *cred, uint16_t index,
        char extra_str[25];
        int i;
 
+       index_list[index].frame++;
+
        if (size < HCI_EVENT_HDR_SIZE) {
                sprintf(extra_str, "(len %d)", size);
                print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
@@ -9093,6 +10251,8 @@ void packet_hci_acldata(struct timeval *tv, struct ucred *cred, uint16_t index,
        uint8_t flags = acl_flags(handle);
        char handle_str[16], extra_str[32];
 
+       index_list[index].frame++;
+
        if (size < HCI_ACL_HDR_SIZE) {
                if (in)
                        print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
@@ -9135,6 +10295,8 @@ void packet_hci_scodata(struct timeval *tv, struct ucred *cred, uint16_t index,
        uint8_t flags = acl_flags(handle);
        char handle_str[16], extra_str[32];
 
+       index_list[index].frame++;
+
        if (size < HCI_SCO_HDR_SIZE) {
                if (in)
                        print_packet(tv, cred, '*', index, NULL, COLOR_ERROR,
@@ -9499,25 +10661,6 @@ static void mgmt_print_uuid(const void *data)
                                get_le32(&uuid[2]), get_le16(&uuid[0]));
 }
 
-static void mgmt_print_enable(const char *label, uint8_t enable)
-{
-       const char *str;
-
-       switch (enable) {
-       case 0x00:
-               str = "Disabled";
-               break;
-       case 0x01:
-               str = "Enabled";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("%s: %s (0x%2.2x)", label, str, enable);
-}
-
 static void mgmt_print_io_capability(uint8_t capability)
 {
        const char *str;
@@ -9660,7 +10803,7 @@ static void mgmt_print_connection_parameter(const void *data)
        mgmt_print_address(data, address_type);
        print_field("Min connection interval: %u", min_conn_interval);
        print_field("Max connection interval: %u", max_conn_interval);
-       print_field("Connection latency: %u", conn_latency);
+       print_conn_latency("Connection latency", conn_latency);
        print_field("Supervision timeout: %u", supv_timeout);
 }
 
@@ -9836,7 +10979,7 @@ static void mgmt_set_powered_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Powered", enable);
+       print_enable("Powered", enable);
 }
 
 static void mgmt_set_discoverable_cmd(const void *data, uint16_t size)
@@ -9868,49 +11011,49 @@ static void mgmt_set_connectable_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Connectable", enable);
+       print_enable("Connectable", enable);
 }
 
 static void mgmt_set_fast_connectable_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Fast Connectable", enable);
+       print_enable("Fast Connectable", enable);
 }
 
 static void mgmt_set_bondable_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Bondable", enable);
+       print_enable("Bondable", enable);
 }
 
 static void mgmt_set_link_security_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Link Security", enable);
+       print_enable("Link Security", enable);
 }
 
 static void mgmt_set_secure_simple_pairing_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Secure Simple Pairing", enable);
+       print_enable("Secure Simple Pairing", enable);
 }
 
 static void mgmt_set_high_speed_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("High Speed", enable);
+       print_enable("High Speed", enable);
 }
 
 static void mgmt_set_low_energy_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Low Energy", enable);
+       print_enable("Low Energy", enable);
 }
 
 static void mgmt_new_settings_rsp(const void *data, uint16_t size)
@@ -9973,7 +11116,7 @@ static void mgmt_load_link_keys_cmd(const void *data, uint16_t size)
        uint16_t num_keys = get_le16(data + 1);
        int i;
 
-       mgmt_print_enable("Debug keys", debug_keys);
+       print_enable("Debug keys", debug_keys);
        print_field("Keys: %u", num_keys);
 
        if (size - 3 != num_keys * 25) {
@@ -10108,7 +11251,7 @@ static void mgmt_unpair_device_cmd(const void *data, uint16_t size)
        uint8_t disconnect = get_u8(data + 7);
 
        mgmt_print_address(data, address_type);
-       mgmt_print_enable("Disconnect", disconnect);
+       print_enable("Disconnect", disconnect);
 }
 
 static void mgmt_unpair_device_rsp(const void *data, uint16_t size)
@@ -10328,7 +11471,7 @@ static void mgmt_set_bredr_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("BR/EDR", enable);
+       print_enable("BR/EDR", enable);
 }
 
 static void mgmt_set_static_address_cmd(const void *data, uint16_t size)
@@ -10551,7 +11694,7 @@ static void mgmt_set_external_configuration_cmd(const void *data, uint16_t size)
 {
        uint8_t enable = get_u8(data);
 
-       mgmt_print_enable("Configuration", enable);
+       print_enable("Configuration", enable);
 }
 
 static void mgmt_set_public_address_cmd(const void *data, uint16_t size)
@@ -11234,7 +12377,7 @@ static void mgmt_discovering_evt(const void *data, uint16_t size)
        uint8_t enable = get_u8(data + 1);
 
        mgmt_print_address_type(type);
-       mgmt_print_enable("Discovery", enable);
+       print_enable("Discovery", enable);
 }
 
 static void mgmt_device_blocked_evt(const void *data, uint16_t size)
@@ -11711,26 +12854,26 @@ void packet_todo(void)
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 void print_le_set_adv_parameters_cmd(const void *data, uint8_t size)
 {
-       le_set_adv_parameters_cmd(data, size);
+        le_set_adv_parameters_cmd(data, size);
 }
 
 void print_le_set_random_address_cmd(const void *data, uint8_t size)
 {
-       le_set_random_address_cmd(data, size);
+        le_set_random_address_cmd(data, size);
 }
 
 void print_le_set_adv_data_cmd(const void *data, uint8_t size)
 {
-       le_set_adv_data_cmd(data, size);
+        le_set_adv_data_cmd(data, size);
 }
 
 void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
 {
-       le_set_scan_rsp_data_cmd(data, size);
+        le_set_scan_rsp_data_cmd(data, size);
 }
 
 void print_le_set_adv_enable_cmd(const void *data, uint8_t size)
 {
-       le_set_adv_enable_cmd(data, size);
+        le_set_adv_enable_cmd(data, size);
 }
 #endif
index 20e1718..2104ca4 100755 (executable)
@@ -33,7 +33,9 @@
 #define PACKET_FILTER_SHOW_TIME_OFFSET (1 << 3)
 #define PACKET_FILTER_SHOW_ACL_DATA    (1 << 4)
 #define PACKET_FILTER_SHOW_SCO_DATA    (1 << 5)
+#define PACKET_FILTER_SHOW_A2DP_STREAM (1 << 6)
 
+bool packet_has_filter(unsigned long filter);
 void packet_set_filter(unsigned long filter);
 void packet_add_filter(unsigned long filter);
 void packet_del_filter(unsigned long filter);
index b32ad40..f3daf95 100755 (executable)
 #include <inttypes.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 
 #include "src/shared/util.h"
 #include "bt.h"
 #include "packet.h"
 #include "display.h"
 #include "l2cap.h"
-#include "uuid.h"
 #include "keys.h"
 #include "sdp.h"
 #include "rfcomm.h"
@@ -90,13 +90,13 @@ struct rfcomm_lhdr {
        uint16_t length;
        uint8_t fcs;
        uint8_t credits; /* only for UIH frame */
-} __attribute__((packed));
+};
 
 struct rfcomm_lmsc {
        uint8_t dlci;
        uint8_t v24_sig;
        uint8_t break_sig;
-} __attribute__((packed));
+};
 
 struct rfcomm_rpn {
        uint8_t dlci;
@@ -106,21 +106,21 @@ struct rfcomm_rpn {
        uint8_t xon;
        uint8_t xoff;
        uint16_t pm;
-} __attribute__ ((packed));
+};
 
 struct rfcomm_rls {
        uint8_t dlci;
        uint8_t error;
-} __attribute__((packed));
+};
 
 struct rfcomm_nsc {
        uint8_t cmd_type;
-} __attribute__((packed));
+};
 
 struct rfcomm_lmcc {
        uint8_t type;
        uint16_t length;
-} __attribute__((packed));
+};
 
 struct rfcomm_frame {
        struct rfcomm_lhdr hdr;
@@ -265,6 +265,7 @@ static inline bool mcc_pn(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
 {
        struct l2cap_frame *frame = &rfcomm_frame->l2cap_frame;
        struct rfcomm_pn pn;
+       uint16_t mtu;
 
        /* rfcomm_pn struct is defined in rfcomm.h */
 
@@ -284,9 +285,11 @@ static inline bool mcc_pn(struct rfcomm_frame *rfcomm_frame, uint8_t indent)
        if (!l2cap_frame_get_u8(frame, &pn.ack_timer))
                return false;
 
-       if (!l2cap_frame_get_le16(frame, &pn.mtu))
+       if (!l2cap_frame_get_le16(frame, &mtu))
                return false;
 
+       pn.mtu = mtu;
+
        if (!l2cap_frame_get_u8(frame, &pn.max_retrans))
                return false;
 
index 7f62332..3fa85c0 100755 (executable)
@@ -32,6 +32,7 @@
 #include <inttypes.h>
 
 #include "lib/bluetooth.h"
+#include "lib/uuid.h"
 
 #include "src/shared/util.h"
 
@@ -39,7 +40,6 @@
 #include "packet.h"
 #include "display.h"
 #include "l2cap.h"
-#include "uuid.h"
 #include "sdp.h"
 
 #define MAX_TID 16
@@ -115,11 +115,11 @@ static void print_uuid(uint8_t indent, const uint8_t *data, uint32_t size)
        switch (size) {
        case 2:
                print_field("%*c%s (0x%4.4x)", indent, ' ',
-                       uuid16_to_str(get_be16(data)), get_be16(data));
+                       bt_uuid16_to_str(get_be16(data)), get_be16(data));
                break;
        case 4:
                print_field("%*c%s (0x%8.8x)", indent, ' ',
-                       uuid32_to_str(get_be32(data)), get_be32(data));
+                       bt_uuid32_to_str(get_be32(data)), get_be32(data));
                break;
        case 16:
                /* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */
@@ -134,7 +134,7 @@ static void print_uuid(uint8_t indent, const uint8_t *data, uint32_t size)
                                get_be16(data + 10) == 0x0080 &&
                                get_be32(data + 12) == 0x5F9B34FB)
                        print_field("%*c%s", indent, ' ',
-                               uuid32_to_str(get_be32(data)));
+                               bt_uuid32_to_str(get_be32(data)));
                break;
        default:
                packet_hexdump(data, size);
index a755b8c..006f15c 100755 (executable)
@@ -317,6 +317,8 @@ static gboolean service_callback(GIOChannel *io, GIOCondition cond,
        if (bt_string2uuid(&uuid, session->service) < 0)
                goto failed;
 
+       sdp_uuid128_to_uuid(&uuid);
+
        search = sdp_list_append(NULL, &uuid);
        attrid = sdp_list_append(NULL, &range);
 
index dd33ee7..a844633 100644 (file)
@@ -144,12 +144,15 @@ static DBusMessage *profile_new_connection(DBusConnection *conn,
        if (fcntl(fd, F_GETFD) < 0) {
                error("bluetooth: fcntl(%d, F_GETFD): %s (%d)", fd,
                                                strerror(errno), errno);
+               close(fd);
                return invalid_args(msg);
        }
 
        io = g_io_channel_unix_new(fd);
-       if (io == NULL)
+       if (io == NULL) {
+               close(fd);
                return invalid_args(msg);
+       }
 
        DBG("device %s", device);
 
index 765f08d..3eca9ef 100755 (executable)
@@ -27,6 +27,7 @@
 
 #include <sys/types.h>
 #include <dirent.h>
+#include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -35,6 +36,8 @@
 
 #include "messages.h"
 
+#define MSG_LIST_XML "mlisting.xml"
+
 static char *root_folder = NULL;
 
 struct session {
@@ -52,6 +55,20 @@ struct folder_listing_data {
        void *user_data;
 };
 
+struct message_listing_data {
+       struct session *session;
+       const char *name;
+       uint16_t max;
+       uint16_t offset;
+       uint8_t subject_len;
+       uint16_t size;
+       char *path;
+       FILE *fp;
+       const struct messages_filter *filter;
+       messages_get_messages_listing_cb callback;
+       void *user_data;
+};
+
 /* NOTE: Neither IrOBEX nor MAP specs says that folder listing needs to
  * be sorted (in IrOBEX examples it is not). However existing implementations
  * seem to follow the fig. 3-2 from MAP specification v1.0, and I've seen a
@@ -155,16 +172,6 @@ static void return_folder_listing(struct folder_listing_data *fld, GSList *list)
        uint16_t num = 0;
        uint16_t offs = 0;
 
-       /* XXX: This isn't really documented for MAP. I need to take a look how
-        * other implementations choose to deal with parent folder.
-        */
-       if (session->cwd[0] != 0 && fld->offset == 0) {
-               num++;
-               fld->callback(session, -EAGAIN, 0, "..", fld->user_data);
-       } else {
-               offs++;
-       }
-
        for (cur = list; offs < fld->offset; offs++) {
                cur = cur->next;
                if (cur == NULL)
@@ -329,6 +336,139 @@ int messages_get_folder_listing(void *s, const char *name, uint16_t max,
        return 0;
 }
 
+static void max_msg_element(GMarkupParseContext *ctxt, const char *element,
+                               const char **names, const char **values,
+                               gpointer user_data, GError **gerr)
+{
+       struct message_listing_data *mld = user_data;
+       const char *key;
+       int i;
+
+       for (i = 0, key = names[i]; key; key = names[++i]) {
+               if (g_strcmp0(names[i], "handle") == 0) {
+                       mld->size++;
+                       break;
+               }
+       }
+}
+
+static void msg_element(GMarkupParseContext *ctxt, const char *element,
+                               const char **names, const char **values,
+                               gpointer user_data, GError **gerr)
+{
+       struct message_listing_data *mld = user_data;
+       struct messages_message *entry = NULL;
+       int i;
+
+       entry = g_new0(struct messages_message, 1);
+       if (mld->filter->parameter_mask == 0) {
+               entry->mask = (entry->mask | PMASK_SUBJECT \
+                       | PMASK_DATETIME | PMASK_RECIPIENT_ADDRESSING \
+                       | PMASK_SENDER_ADDRESSING \
+                       | PMASK_ATTACHMENT_SIZE | PMASK_TYPE \
+                       | PMASK_RECEPTION_STATUS);
+       } else
+               entry->mask = mld->filter->parameter_mask;
+
+       for (i = 0 ; names[i]; ++i) {
+               if (g_strcmp0(names[i], "handle") == 0) {
+                       entry->handle = g_strdup(values[i]);
+                       mld->size++;
+                       continue;
+               }
+               if (g_strcmp0(names[i], "attachment_size") == 0) {
+                       entry->attachment_size = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "datetime") == 0) {
+                       entry->datetime = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "subject") == 0) {
+                       entry->subject = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "recipient_addressing") == 0) {
+                       entry->recipient_addressing = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "sender_addressing") == 0) {
+                       entry->sender_addressing = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "type") == 0) {
+                       entry->type = g_strdup(values[i]);
+                       continue;
+               }
+               if (g_strcmp0(names[i], "reception_status") == 0)
+                       entry->reception_status = g_strdup(values[i]);
+       }
+
+       if (mld->size > mld->offset)
+               mld->callback(mld->session, -EAGAIN, mld->size, 0, entry, mld->user_data);
+
+       g_free(entry->reception_status);
+       g_free(entry->type);
+       g_free(entry->sender_addressing);
+       g_free(entry->subject);
+       g_free(entry->datetime);
+       g_free(entry->attachment_size);
+       g_free(entry->handle);
+       g_free(entry);
+}
+
+static const GMarkupParser msg_parser = {
+        msg_element,
+        NULL,
+        NULL,
+        NULL,
+        NULL
+};
+
+static const GMarkupParser max_msg_parser = {
+        max_msg_element,
+        NULL,
+        NULL,
+        NULL,
+        NULL
+};
+
+static gboolean get_messages_listing(void *d)
+{
+
+       struct message_listing_data *mld = d;
+       /* 1024 is the maximum size of the line which is calculated to be more
+        * sufficient*/
+       char buffer[1024];
+       GMarkupParseContext *ctxt;
+       size_t len;
+
+       while (fgets(buffer, 1024, mld->fp)) {
+               len = strlen(buffer);
+
+               if (mld->max == 0) {
+                       ctxt = g_markup_parse_context_new(&max_msg_parser, 0, mld, NULL);
+                       g_markup_parse_context_parse(ctxt, buffer, len, NULL);
+                       g_markup_parse_context_free(ctxt);
+               } else {
+                       ctxt = g_markup_parse_context_new(&msg_parser, 0, mld, NULL);
+                       g_markup_parse_context_parse(ctxt, buffer, len, NULL);
+                       g_markup_parse_context_free(ctxt);
+               }
+       }
+
+       if (mld->max == 0) {
+               mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
+               goto done;
+       }
+
+       mld->callback(mld->session, 0, mld->size, 0, NULL, mld->user_data);
+
+done:
+       fclose(mld->fp);
+       return FALSE;
+}
+
 int messages_get_messages_listing(void *session, const char *name,
                                uint16_t max, uint16_t offset,
                                uint8_t subject_len,
@@ -336,7 +476,41 @@ int messages_get_messages_listing(void *session, const char *name,
                                messages_get_messages_listing_cb callback,
                                void *user_data)
 {
-       return -ENOSYS;
+       struct message_listing_data *mld;
+       struct session *s =  session;
+       char *path;
+
+       mld = g_new0(struct message_listing_data, 1);
+       mld->session = s;
+       mld->name = name;
+       mld->max = max;
+       mld->offset = offset;
+       mld->subject_len = subject_len;
+       mld->callback = callback;
+       mld->filter = filter;
+       mld->user_data = user_data;
+
+       path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
+       mld->fp = fopen(path, "r");
+       if (mld->fp == NULL) {
+               g_free(path);
+               messages_set_folder(s, mld->name, 0);
+               path = g_build_filename(s->cwd_absolute, MSG_LIST_XML, NULL);
+               mld->fp = fopen(path, "r");
+               if (mld->fp == NULL) {
+                       int err = -errno;
+                       DBG("fopen(): %d, %s", -err, strerror(-err));
+                       g_free(path);
+                       return -EBADR;
+               }
+       }
+
+
+       g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, get_messages_listing,
+                                                               mld, g_free);
+       g_free(path);
+
+       return 0;
 }
 
 int messages_get_message(void *session, const char *handle,
index 68a2139..7f63d10 100755 (executable)
@@ -390,6 +390,7 @@ static int generate_response(void *user_data)
                /* Ignore all other parameter and return PhoneBookSize */
                uint16_t size = g_slist_length(pbap->cache.entries);
 
+               pbap->obj->firstpacket = TRUE;
                pbap->obj->apparam = g_obex_apparam_set_uint16(
                                                        pbap->obj->apparam,
                                                        PHONEBOOKSIZE_TAG,
@@ -1032,8 +1033,10 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
 
        *hi = G_OBEX_HDR_APPARAM;
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
-       if (pbap->params->maxlistcount == 0)
+       if (obj->firstpacket) {
+               obj->firstpacket = FALSE;
                return g_obex_apparam_encode(obj->apparam, buf, mtu);
+       }
 
        return 0;
 #else
index 37f7c85..dc7c3b3 100755 (executable)
@@ -143,6 +143,7 @@ static void add_slash(char *dest, const char *src, int len_max, int len)
                                goto done;
 
                        dest[j++] = '\\';
+                       /* fall through */
                default:
                        dest[j] = src[i];
                        break;
index 92479df..f082032 100755 (executable)
@@ -311,6 +311,9 @@ static DBusMessage *transfer_cancel(DBusConnection *connection,
        struct obex_session *os = transfer->session;
        const char *sender;
 
+       if (!agent)
+               return agent_does_not_exist(msg);
+
        if (!os)
                return invalid_args(msg);
 
index b81dfd9..33976dc 100644 (file)
@@ -239,8 +239,10 @@ static void obex_session_free(struct obex_session *os)
 {
        sessions = g_slist_remove(sessions, os);
 
-       if (os->io)
+       if (os->io) {
+               g_io_channel_shutdown(os->io, TRUE, NULL);
                g_io_channel_unref(os->io);
+       }
 
        if (os->obex)
                g_obex_unref(os->obex);
@@ -882,6 +884,17 @@ static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data)
                return;
        }
 
+       /* OPP session don't require CONNECT, in which case just call connect
+        * callback to register the transfer.
+        */
+       if (!os->service_data && os->service->service == OBEX_OPP) {
+               os->service_data = os->service->connect(os, &err);
+               if (err < 0) {
+                       os_set_response(os, err);
+                       return;
+               }
+       }
+
        parse_type(os, req);
 
        if (os->driver == NULL) {
index 258b900..185ecb6 100755 (executable)
@@ -22,6 +22,8 @@
  *
  */
 
+#include <unistd.h>
+
 #define OBJECT_SIZE_UNKNOWN -1
 #define OBJECT_SIZE_DELETE -2
 
index 702bd22..91f6b6a 100755 (executable)
@@ -9,7 +9,7 @@
 %define upgrade_script_path /usr/share/upgrade/scripts
 Name:          bluez
 Summary:       Bluetooth Stack for Linux
-Version:       5.43
+Version:       5.48
 Release:       1
 Group:         Network & Connectivity/Bluetooth
 License:       GPL-2.0+ and LGPL-2.1+ and Apache-2.0
@@ -31,6 +31,7 @@ Source1001:     bluez.manifest
 BuildRequires:  pkgconfig(dbus-1)
 BuildRequires:  pkgconfig(iniparser)
 BuildRequires:  pkgconfig(libxml-2.0)
+BuildRequires:  pkgconfig(json-c)
 #BuildRequires:  pkgconfig(glib-2.0)
 #BuildRequires:  pkgconfig(ncurses)
 #BuildRequires:  flex
@@ -193,6 +194,7 @@ export CFLAGS+=" -DARCH64"
                        --enable-tds=yes \
                        --enable-dbusoob \
                        --enable-test \
+                       --enable-mesh=yes \
                        --with-telephony=tizen \
                        --enable-obex \
                        --enable-library \
@@ -252,6 +254,7 @@ export CFLAGS+=" -DARCH64"
                        --enable-health=yes \
                        --enable-dbusoob \
                        --enable-test \
+                       --enable-mesh=yes \
                        --with-telephony=tizen \
                        --enable-obex \
                        --enable-library \
@@ -316,6 +319,7 @@ export CFLAGS+=" -DARCH64"
                        --enable-test \
                        --with-telephony=tizen \
                        --enable-obex \
+                       --enable-mesh=yes \
                        --enable-library \
                        --enable-gatt \
                        --enable-experimental \
@@ -371,6 +375,7 @@ install -D -m 0755 tools/btmgmt $RPM_BUILD_ROOT/%{_bindir}/
 install -D -m 0755 tools/scotest $RPM_BUILD_ROOT/%{_bindir}/
 install -D -m 0755 tools/bluemoon $RPM_BUILD_ROOT/%{_bindir}/
 install -D -m 0755 attrib/gatttool $RPM_BUILD_ROOT/%{_bindir}/
+install -D -m 0755 mesh/meshctl $RPM_BUILD_ROOT/%{_bindir}/
 
 
 install -D -m 0755 tools/obexctl %{buildroot}%{_bindir}/obexctl
@@ -484,6 +489,7 @@ popd
 %{_bindir}/l2test
 %{_bindir}/rctest
 %{_bindir}/bluetoothctl
+%{_bindir}/meshctl
 %{_bindir}/btiotest
 #%{_bindir}/mpris-player
 %{_bindir}/bluetooth-player
index 4c5531d..5ae19a8 100755 (executable)
@@ -145,8 +145,8 @@ static struct gatt_conn *gatt_conn_new(int fd)
                return NULL;
        }
 
-       bt_gatt_client_set_ready_handler(conn->client,
-                               client_ready_callback, conn, NULL);
+       bt_gatt_client_ready_register(conn->client, client_ready_callback,
+                                                               conn, NULL);
        bt_gatt_client_set_service_changed(conn->client,
                                client_service_changed_callback, conn, NULL);
 
index c201722..6980b0a 100755 (executable)
@@ -60,13 +60,23 @@ static ssize_t autopair_pincb(struct btd_adapter *adapter,
 {
        char addr[18];
        char pinstr[7];
+       char name[25];
        uint32_t class;
 
        ba2str(device_get_address(device), addr);
 
        class = btd_device_get_class(device);
 
-       DBG("device %s 0x%x", addr, class);
+       device_get_name(device, name, sizeof(name));
+
+       DBG("device '%s' (%s) class: 0x%x vid/pid: 0x%X/0x%X",
+               name, addr, class,
+               btd_device_get_vendor (device),
+               btd_device_get_product (device));
+
+       /* The iCade shouldn't use random PINs like normal keyboards */
+       if (name != NULL && strstr(name, "iCade") != NULL)
+               return 0;
 
        /* This is a class-based pincode guesser. Ignore devices with an
         * unknown class.
@@ -110,7 +120,7 @@ static ssize_t autopair_pincb(struct btd_adapter *adapter,
                        if (attempt >= 4)
                                return 0;
 
-                       snprintf(pinstr, sizeof(pinstr), "%06d",
+                       snprintf(pinstr, sizeof(pinstr), "%06u",
                                                rand() % 1000000);
                        *display = true;
                        memcpy(pinbuf, pinstr, 6);
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
deleted file mode 100755 (executable)
index f8f3551..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011  ST-Ericsson SA
- *
- *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include "gdbus/gdbus.h"
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
-
-#include "src/plugin.h"
-#include "src/log.h"
-#include "src/dbus-common.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/eir.h"
-#include "src/agent.h"
-#include "src/hcid.h"
-#include "src/error.h"
-
-#define OOB_INTERFACE  "org.bluez.OutOfBand"
-
-struct oob_request {
-       struct btd_adapter *adapter;
-       DBusMessage *msg;
-};
-
-static GSList *oob_requests = NULL;
-static DBusConnection *connection = NULL;
-
-static gint oob_request_cmp(gconstpointer a, gconstpointer b)
-{
-       const struct oob_request *data = a;
-       const struct btd_adapter *adapter = b;
-
-       return data->adapter != adapter;
-}
-
-static struct oob_request *find_oob_request(struct btd_adapter *adapter)
-{
-       GSList *match;
-
-       match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
-
-       if (match)
-               return match->data;
-
-       return NULL;
-}
-
-static void read_local_data_complete(struct btd_adapter *adapter,
-                       const uint8_t *hash192, const uint8_t *randomizer192,
-                       const uint8_t *hash256, const uint8_t *randomizer256,
-                       void *user_data)
-{
-       struct DBusMessage *reply;
-       struct oob_request *oob_request;
-
-       oob_request = find_oob_request(adapter);
-       if (!oob_request)
-               return;
-
-       if ((hash192 && randomizer192) || (hash256 && randomizer256))
-               reply = g_dbus_create_reply(oob_request->msg,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, 16,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, 16,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
-                               &hash256, hash256 ? 16 : 0,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
-                               &randomizer256, randomizer256 ? 16 : 0,
-                       DBUS_TYPE_INVALID);
-       else
-               reply = btd_error_failed(oob_request->msg,
-                                       "Failed to read local OOB data.");
-
-       oob_requests = g_slist_remove(oob_requests, oob_request);
-       dbus_message_unref(oob_request->msg);
-       g_free(oob_request);
-
-       if (!reply) {
-               error("Couldn't allocate D-Bus message");
-               return;
-       }
-
-       if (!g_dbus_send_message(connection, reply))
-               error("D-Bus send failed");
-}
-
-static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
-                                                               void *data)
-{
-       struct btd_adapter *adapter = data;
-       struct oob_request *oob_request;
-       struct oob_handler *handler;
-
-       if (find_oob_request(adapter))
-               return btd_error_in_progress(msg);
-
-       if (btd_adapter_read_local_oob_data(adapter))
-               return btd_error_failed(msg, "Request failed.");
-
-       oob_request = g_new(struct oob_request, 1);
-       oob_request->adapter = adapter;
-       oob_requests = g_slist_append(oob_requests, oob_request);
-       oob_request->msg = dbus_message_ref(msg);
-
-       handler = g_new0(struct oob_handler, 1);
-       handler->read_local_cb = read_local_data_complete;
-
-       btd_adapter_set_oob_handler(oob_request->adapter, handler);
-
-       return NULL;
-}
-
-static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
-                                                               void *data)
-{
-       struct btd_adapter *adapter = data;
-       const char *addr = NULL;
-       uint8_t *hash192 = NULL;
-       uint8_t *randomizer192 = NULL;
-       int32_t h192_len = 0;
-       int32_t r192_len = 0;
-       uint8_t *hash256 = NULL;
-       uint8_t *randomizer256 = NULL;
-       int32_t h256_len = 0;
-       int32_t r256_len = 0;
-       bdaddr_t bdaddr;
-       uint8_t addr_type = BDADDR_BREDR;
-       bool valid_len;
-
-       if (!dbus_message_get_args(msg, NULL,
-                       DBUS_TYPE_STRING, &addr,
-                       DBUS_TYPE_BYTE, &addr_type,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, &h192_len,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, &r192_len,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash256, &h256_len,
-                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer256, &r256_len,
-                       DBUS_TYPE_INVALID))
-               return btd_error_invalid_args(msg);
-
-       DBG("address type: %d", addr_type);
-
-       valid_len = (h192_len == 16 && r192_len == 16) ||
-               (h256_len == 16 && r256_len == 16);
-
-       if (!valid_len || bachk(addr))
-               return btd_error_invalid_args(msg);
-
-       str2ba(addr, &bdaddr);
-
-       if (btd_adapter_add_remote_oob_ext_data(adapter, &bdaddr, addr_type,
-                               hash192, randomizer192,
-                               hash256, randomizer256))
-               return btd_error_failed(msg, "Request failed");
-
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
-                                                               void *data)
-{
-       struct btd_adapter *adapter = data;
-       const char *addr;
-       bdaddr_t bdaddr;
-       uint8_t addr_type = 0;
-
-       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
-                       DBUS_TYPE_INVALID))
-               return btd_error_invalid_args(msg);
-
-       if (bachk(addr))
-               return btd_error_invalid_args(msg);
-
-       str2ba(addr, &bdaddr);
-
-       if (btd_adapter_remove_remote_oob_ext_data(adapter, &bdaddr, addr_type))
-               return btd_error_failed(msg, "Request failed");
-
-       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static const GDBusMethodTable oob_methods[] = {
-       { GDBUS_METHOD("AddRemoteData",
-                       GDBUS_ARGS({ "address", "s" },
-                               { "address_type", "y" },
-                               { "hash192", "ay" }, { "randomizer192", "ay" },
-                               { "hash256", "ay" }, { "randomizer256", "ay" }),
-                       NULL,
-                       add_remote_data) },
-       { GDBUS_METHOD("RemoveRemoteData",
-                       GDBUS_ARGS({ "address", "s" }), NULL,
-                       remove_remote_data) },
-       { GDBUS_ASYNC_METHOD("ReadLocalData",
-                       NULL, GDBUS_ARGS(
-                               {"hash192", "ay" }, { "randomizer192", "ay" },
-                               {"hash256", "ay" }, { "randomizer256", "ay" }),
-                       read_local_data) },
-       { }
-};
-
-static int oob_probe(struct btd_adapter *adapter)
-{
-       const char *path = adapter_get_path(adapter);
-
-       DBG("dbusoob probe");
-       DBG("adapter path: %s", path);
-
-       if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
-                               oob_methods, NULL, NULL, adapter, NULL)) {
-                       error("OOB interface init failed on path %s", path);
-                       return -EIO;
-               }
-
-       return 0;
-}
-
-static void oob_remove(struct btd_adapter *adapter)
-{
-       read_local_data_complete(adapter, NULL, NULL, NULL, NULL, NULL);
-
-       g_dbus_unregister_interface(connection, adapter_get_path(adapter),
-                                                       OOB_INTERFACE);
-}
-
-static struct btd_adapter_driver oob_driver = {
-       .name   = "oob",
-       .probe  = oob_probe,
-       .remove = oob_remove,
-};
-
-static int dbusoob_init(void)
-{
-       DBG("Setup dbusoob plugin");
-
-       connection = btd_get_dbus_connection();
-
-       return btd_register_adapter_driver(&oob_driver);
-}
-
-static void dbusoob_exit(void)
-{
-       DBG("Cleanup dbusoob plugin");
-
-       btd_unregister_adapter_driver(&oob_driver);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
-                                               dbusoob_init, dbusoob_exit)
-#endif
index 31af6a3..51e276e 100755 (executable)
@@ -20,6 +20,7 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
index 9fdee00..68effff 100755 (executable)
@@ -221,7 +221,7 @@ static gboolean policy_connect_sink(gpointer user_data)
        struct policy_data *data = user_data;
        struct btd_service *service;
 
-       data->source_timer = 0;
+       data->sink_timer = 0;
        data->sink_retries++;
 
        service = btd_device_get_service(data->dev, A2DP_SINK_UUID);
@@ -677,7 +677,7 @@ static void service_cb(struct btd_service *service,
         */
        reconnect = reconnect_add(service);
 
-       reconnect_reset(reconnect);
+       reconnect->active = false;
 
        /*
         * Should this device be reconnected? A matching UUID might not
@@ -806,7 +806,7 @@ static int policy_init(void)
                reconnect_intervals_len = sizeof(default_intervals) /
                                                sizeof(*reconnect_intervals);
                reconnect_intervals = g_memdup(default_intervals,
-                                               reconnect_intervals_len);
+                                               sizeof(default_intervals));
                goto done;
        }
 
@@ -834,9 +834,10 @@ static int policy_init(void)
                                        &gerr);
        if (gerr) {
                g_clear_error(&gerr);
-               reconnect_intervals_len = sizeof(default_intervals);
+               reconnect_intervals_len = sizeof(default_intervals) /
+                                               sizeof(*reconnect_intervals);
                reconnect_intervals = g_memdup(default_intervals,
-                                               reconnect_intervals_len);
+                                               sizeof(default_intervals));
        }
 
        auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
index fcc93bc..9290dbe 100755 (executable)
 
 #include "src/adapter.h"
 #include "src/device.h"
+#include "src/agent.h"
 #include "src/plugin.h"
 #include "src/log.h"
 #include "src/shared/util.h"
+#include "profiles/input/sixaxis.h"
 
-static const struct {
-       const char *name;
-       uint16_t source;
-       uint16_t vid;
-       uint16_t pid;
-       uint16_t version;
-} devices[] = {
-       {
-               .name = "PLAYSTATION(R)3 Controller",
-               .source = 0x0002,
-               .vid = 0x054c,
-               .pid = 0x0268,
-               .version = 0x0000,
-       },
-       {
-               .name = "Navigation Controller",
-               .source = 0x0002,
-               .vid = 0x054c,
-               .pid = 0x042f,
-               .version = 0x0000,
-       },
+struct authentication_closure {
+       guint auth_id;
+       char *sysfs_path;
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       int fd;
+       bdaddr_t bdaddr; /* device bdaddr */
+       CablePairingType type;
 };
 
-struct leds_data {
-       char *syspath_prefix;
-       uint8_t bitmap;
+struct authentication_destroy_closure {
+       struct authentication_closure *closure;
+       bool remove_device;
 };
 
-static void leds_data_destroy(struct leds_data *data)
-{
-       free(data->syspath_prefix);
-       free(data);
-}
-
 static struct udev *ctx = NULL;
 static struct udev_monitor *monitor = NULL;
 static guint watch_id = 0;
+/* key = sysfs_path (const str), value = auth_closure */
+static GHashTable *pending_auths = NULL;
+
+#define SIXAXIS_HID_SDP_RECORD "3601920900000A000100000900013503191124090004"\
+       "350D35061901000900113503190011090006350909656E09006A090100090009350"\
+       "8350619112409010009000D350F350D350619010009001335031900110901002513"\
+       "576972656C65737320436F6E74726F6C6C65720901012513576972656C657373204"\
+       "36F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E746572"\
+       "7461696E6D656E74090200090100090201090100090202080009020308210902042"\
+       "8010902052801090206359A35980822259405010904A101A1028501750895011500"\
+       "26FF00810375019513150025013500450105091901291381027501950D0600FF810"\
+       "3150026FF0005010901A10075089504350046FF0009300931093209358102C00501"\
+       "75089527090181027508953009019102750895300901B102C0A1028502750895300"\
+       "901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C00902"\
+       "07350835060904090901000902082800090209280109020A280109020B090100090"\
+       "20C093E8009020D280009020E2800"
+
+/* Make sure to unset auth_id if already handled */
+static void auth_closure_destroy(struct authentication_closure *closure,
+                               bool remove_device)
+{
+       if (closure->auth_id)
+               btd_cancel_authorization(closure->auth_id);
+
+       if (remove_device)
+               btd_adapter_remove_device(closure->adapter, closure->device);
+       close(closure->fd);
+       g_free(closure->sysfs_path);
+       g_free(closure);
+}
 
-static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+static int sixaxis_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
 {
        uint8_t buf[18];
        int ret;
@@ -107,422 +120,348 @@ static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
        return 0;
 }
 
-static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+static int ds4_get_device_bdaddr(int fd, bdaddr_t *bdaddr)
 {
-       uint8_t buf[8];
+       uint8_t buf[7];
        int ret;
 
        memset(buf, 0, sizeof(buf));
 
-       buf[0] = 0xf5;
+       buf[0] = 0x81;
 
        ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
        if (ret < 0) {
-               error("sixaxis: failed to read master address (%s)",
-                                                       strerror(errno));
+               error("sixaxis: failed to read DS4 device address (%s)",
+                     strerror(errno));
                return ret;
        }
 
-       baswap(bdaddr, (bdaddr_t *) (buf + 2));
+       /* address is little-endian on DS4 */
+       bacpy(bdaddr, (bdaddr_t*) (buf + 1));
 
        return 0;
 }
 
-static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+static int get_device_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+       if (type == CABLE_PAIRING_SIXAXIS)
+               return sixaxis_get_device_bdaddr(fd, bdaddr);
+       else if (type == CABLE_PAIRING_DS4)
+               return ds4_get_device_bdaddr(fd, bdaddr);
+       return -1;
+}
+
+static int sixaxis_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
 {
        uint8_t buf[8];
        int ret;
 
-       buf[0] = 0xf5;
-       buf[1] = 0x01;
+       memset(buf, 0, sizeof(buf));
 
-       baswap((bdaddr_t *) (buf + 2), bdaddr);
+       buf[0] = 0xf5;
 
-       ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
-       if (ret < 0)
-               error("sixaxis: failed to write master address (%s)",
+       ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+       if (ret < 0) {
+               error("sixaxis: failed to read master address (%s)",
                                                        strerror(errno));
+               return ret;
+       }
 
-       return ret;
+       baswap(bdaddr, (bdaddr_t *) (buf + 2));
+
+       return 0;
 }
 
-static uint8_t calc_leds_bitmap(int number)
+static int ds4_get_master_bdaddr(int fd, bdaddr_t *bdaddr)
 {
-       uint8_t bitmap = 0;
+       uint8_t buf[16];
+       int ret;
 
-       /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
-       if (number > 7)
-               return bitmap;
+       memset(buf, 0, sizeof(buf));
+
+       buf[0] = 0x12;
 
-       if (number > 4) {
-               bitmap |= 0x10;
-               number -= 4;
+       ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+       if (ret < 0) {
+               error("sixaxis: failed to read DS4 master address (%s)",
+                     strerror(errno));
+               return ret;
        }
 
-       bitmap |= 0x01 << number;
+       /* address is little-endian on DS4 */
+       bacpy(bdaddr, (bdaddr_t*) (buf + 10));
 
-       return bitmap;
+       return 0;
+}
+
+static int get_master_bdaddr(int fd, bdaddr_t *bdaddr, CablePairingType type)
+{
+       if (type == CABLE_PAIRING_SIXAXIS)
+               return sixaxis_get_master_bdaddr(fd, bdaddr);
+       else if (type == CABLE_PAIRING_DS4)
+               return ds4_get_master_bdaddr(fd, bdaddr);
+       return -1;
 }
 
-static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
+static int sixaxis_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
 {
-       /*
-        * the total time the led is active (0xff means forever)
-        * |     duty_length: cycle time in deciseconds (0 - "blink very fast")
-        * |     |     ??? (Maybe a phase shift or duty_length multiplier?)
-        * |     |     |     % of duty_length led is off (0xff means 100%)
-        * |     |     |     |     % of duty_length led is on (0xff means 100%)
-        * |     |     |     |     |
-        * 0xff, 0x27, 0x10, 0x00, 0x32,
-        */
-       uint8_t leds_report[] = {
-               0x01,
-               0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
-               0x00, 0x00, 0x00, 0x00, 0x00, /* LED_1=0x02, LED_2=0x04 ... */
-               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_4 */
-               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_3 */
-               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_2 */
-               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */
-               0x00, 0x00, 0x00, 0x00, 0x00,
-       };
+       uint8_t buf[8];
        int ret;
 
-       leds_report[10] = leds_bitmap;
+       buf[0] = 0xf5;
+       buf[1] = 0x01;
 
-       ret = write(fd, leds_report, sizeof(leds_report));
-       if (ret == sizeof(leds_report))
-               return;
+       baswap((bdaddr_t *) (buf + 2), bdaddr);
 
+       ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
        if (ret < 0)
-               error("sixaxis: failed to set LEDS (%s)", strerror(errno));
-       else
-               error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+               error("sixaxis: failed to write master address (%s)",
+                                                       strerror(errno));
+
+       return ret;
 }
 
-static bool set_leds_sysfs(struct leds_data *data)
+static int ds4_set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
 {
-       int i;
+       uint8_t buf[23];
+       int ret;
 
-       if (!data->syspath_prefix)
-               return false;
+       buf[0] = 0x13;
+       bacpy((bdaddr_t*) (buf + 1), bdaddr);
+       /* TODO: we could put the key here but
+          there is no way to force a re-loading
+          of link keys to the kernel from here. */
+       memset(buf + 7, 0, 16);
 
-       /* start from 1, LED0 is never used */
-       for (i = 1; i <= 4; i++) {
-               char path[PATH_MAX] = { 0 };
-               char buf[2] = { 0 };
-               int fd;
-               int ret;
+       ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
+       if (ret < 0)
+               error("sixaxis: failed to write DS4 master address (%s)",
+                     strerror(errno));
 
-               snprintf(path, PATH_MAX, "%s%d/brightness",
-                                               data->syspath_prefix, i);
+       return ret;
+}
 
-               fd = open(path, O_WRONLY);
-               if (fd < 0) {
-                       error("sixaxis: cannot open %s (%s)", path,
-                                                       strerror(errno));
-                       return false;
-               }
+static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr,
+                                       CablePairingType type)
+{
+       if (type == CABLE_PAIRING_SIXAXIS)
+               return sixaxis_set_master_bdaddr(fd, bdaddr);
+       else if (type == CABLE_PAIRING_DS4)
+               return ds4_set_master_bdaddr(fd, bdaddr);
+       return -1;
+}
 
-               buf[0] = '0' + !!(data->bitmap & (1 << i));
-               ret = write(fd, buf, sizeof(buf));
-               close(fd);
-               if (ret != sizeof(buf))
-                       return false;
+static bool is_auth_pending(struct authentication_closure *closure)
+{
+       GHashTableIter iter;
+       gpointer value;
+
+       g_hash_table_iter_init(&iter, pending_auths);
+       while (g_hash_table_iter_next(&iter, NULL, &value)) {
+               struct authentication_closure *c = value;
+               if (c == closure)
+                       return true;
        }
-
-       return true;
+       return false;
 }
 
-static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
-                                                       gpointer user_data)
+static gboolean auth_closure_destroy_idle(gpointer user_data)
 {
-       struct leds_data *data = user_data;
+       struct authentication_destroy_closure *destroy = user_data;
 
-       if (!data)
-               return FALSE;
+       auth_closure_destroy(destroy->closure, destroy->remove_device);
+       g_free(destroy);
 
-       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
-               goto out;
-
-       if(!set_leds_sysfs(data)) {
-               int fd = g_io_channel_unix_get_fd(channel);
-               set_leds_hidraw(fd, data->bitmap);
-       }
-
-out:
-       leds_data_destroy(data);
-
-       return FALSE;
+       return false;
 }
 
-static bool setup_device(int fd, int index, struct btd_adapter *adapter)
+static void agent_auth_cb(DBusError *derr, void *user_data)
 {
-       char device_addr[18], master_addr[18], adapter_addr[18];
-       bdaddr_t device_bdaddr, master_bdaddr;
+       struct authentication_closure *closure = user_data;
+       struct authentication_destroy_closure *destroy;
+       char master_addr[18], adapter_addr[18], device_addr[18];
+       bdaddr_t master_bdaddr;
        const bdaddr_t *adapter_bdaddr;
-       struct btd_device *device;
+       bool remove_device = true;
 
-       if (get_device_bdaddr(fd, &device_bdaddr) < 0)
-               return false;
+       if (!is_auth_pending(closure))
+               return;
 
-       if (get_master_bdaddr(fd, &master_bdaddr) < 0)
-               return false;
+       /* Don't try to remove this auth, we're handling it already */
+       closure->auth_id = 0;
 
-       /* This can happen if controller was plugged while already connected
-        * eg. to charge up battery.
-        * Don't set LEDs in that case, hence return false */
-       device = btd_adapter_find_device(adapter, &device_bdaddr,
-                                                       BDADDR_BREDR);
-       if (device && btd_device_is_connected(device))
-               return false;
+       if (derr != NULL) {
+               DBG("Agent replied negatively, removing temporary device");
+               goto out;
+       }
 
-       adapter_bdaddr = btd_adapter_get_address(adapter);
+       if (get_master_bdaddr(closure->fd, &master_bdaddr, closure->type) < 0)
+               goto out;
 
+       adapter_bdaddr = btd_adapter_get_address(closure->adapter);
        if (bacmp(adapter_bdaddr, &master_bdaddr)) {
-               if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
-                       return false;
+               if (set_master_bdaddr(closure->fd, adapter_bdaddr,
+                                                       closure->type) < 0)
+                       goto out;
        }
 
-       ba2str(&device_bdaddr, device_addr);
+       remove_device = false;
+       btd_device_set_trusted(closure->device, true);
+       btd_device_set_temporary(closure->device, false);
+
+       if (closure->type == CABLE_PAIRING_SIXAXIS)
+               btd_device_set_record(closure->device, HID_UUID,
+                                                SIXAXIS_HID_SDP_RECORD);
+
+       ba2str(&closure->bdaddr, device_addr);
        ba2str(&master_bdaddr, master_addr);
        ba2str(adapter_bdaddr, adapter_addr);
        DBG("remote %s old_master %s new_master %s",
                                device_addr, master_addr, adapter_addr);
 
-       device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
-
-       if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
-                                               (GCompareFunc)strcasecmp)) {
-               DBG("device %s already known, skipping", device_addr);
-               return true;
-       }
-
-       info("sixaxis: setting up new device");
-
-       btd_device_device_set_name(device, devices[index].name);
-       btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
-                               devices[index].pid, devices[index].version);
-       btd_device_set_temporary(device, false);
-
-       return true;
-}
-
-static int get_js_number(struct udev_device *udevice)
-{
-       struct udev_list_entry *devices, *dev_list_entry;
-       struct udev_enumerate *enumerate;
-       struct udev_device *hid_parent;
-       const char *hidraw_node;
-       const char *hid_id;
-       int number = 0;
-
-       hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
-                                                               "hid", NULL);
-
-       /*
-        * Look for HID_UNIQ first for the correct behavior via BT, if
-        * HID_UNIQ is not available it means the USB bus is being used and we
-        * can rely on HID_PHYS.
-        */
-       hid_id = udev_device_get_property_value(hid_parent, "HID_UNIQ");
-       if (!hid_id)
-               hid_id = udev_device_get_property_value(hid_parent,
-                                                       "HID_PHYS");
-
-       hidraw_node = udev_device_get_devnode(udevice);
-       if (!hid_id || !hidraw_node)
-               return 0;
-
-       enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
-       udev_enumerate_add_match_sysname(enumerate, "js*");
-       udev_enumerate_scan_devices(enumerate);
-       devices = udev_enumerate_get_list_entry(enumerate);
-
-       udev_list_entry_foreach(dev_list_entry, devices) {
-               struct udev_device *input_parent;
-               struct udev_device *js_dev;
-               const char *input_id;
-               const char *devname;
-
-               devname = udev_list_entry_get_name(dev_list_entry);
-               js_dev = udev_device_new_from_syspath(
-                                               udev_device_get_udev(udevice),
-                                               devname);
-
-               input_parent = udev_device_get_parent_with_subsystem_devtype(
-                                                       js_dev, "input", NULL);
-               if (!input_parent)
-                       goto next;
-
-               /* check if this is the joystick relative to the hidraw device
-                * above */
-               input_id = udev_device_get_sysattr_value(input_parent, "uniq");
-
-               /*
-                * A strlen() check is needed because input device over USB
-                * have the UNIQ attribute defined but with an empty value.
-                */
-               if (!input_id || strlen(input_id) == 0)
-                       input_id = udev_device_get_sysattr_value(input_parent,
-                                                                "phys");
-
-               if (!input_id)
-                       goto next;
-
-               if (!strcmp(input_id, hid_id)) {
-                       number = atoi(udev_device_get_sysnum(js_dev));
-
-                       /* joystick numbers start from 0, leds from 1 */
-                       number++;
-
-                       udev_device_unref(js_dev);
-                       break;
-               }
-next:
-               udev_device_unref(js_dev);
-       }
-
-       udev_enumerate_unref(enumerate);
-
-       return number;
+out:
+       g_hash_table_steal(pending_auths, closure->sysfs_path);
+
+       /* btd_adapter_remove_device() cannot be called in this
+        * callback or it would lead to a double-free in while
+        * trying to cancel the authentication that's being processed,
+        * so clean up in an idle */
+       destroy = g_new0(struct authentication_destroy_closure, 1);
+       destroy->closure = closure;
+       destroy->remove_device = remove_device;
+       g_idle_add(auth_closure_destroy_idle, destroy);
 }
 
-static char *get_leds_syspath_prefix(struct udev_device *udevice)
+static bool setup_device(int fd, const char *sysfs_path,
+                       const struct cable_pairing *cp,
+                       struct btd_adapter *adapter)
 {
-       struct udev_list_entry *dev_list_entry;
-       struct udev_enumerate *enumerate;
-       struct udev_device *hid_parent;
-       const char *syspath;
-       char *syspath_prefix;
-
-       hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
-                                                               "hid", NULL);
+       bdaddr_t device_bdaddr;
+       const bdaddr_t *adapter_bdaddr;
+       struct btd_device *device;
+       struct authentication_closure *closure;
 
-       enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
-       udev_enumerate_add_match_parent(enumerate, hid_parent);
-       udev_enumerate_add_match_subsystem(enumerate, "leds");
-       udev_enumerate_scan_devices(enumerate);
+       if (get_device_bdaddr(fd, &device_bdaddr, cp->type) < 0)
+               return false;
 
-       dev_list_entry = udev_enumerate_get_list_entry(enumerate);
-       if (!dev_list_entry) {
-               syspath_prefix = NULL;
-               goto out;
+       /* This can happen if controller was plugged while already setup and
+        * connected eg. to charge up battery. */
+       device = btd_adapter_find_device(adapter, &device_bdaddr,
+                                                       BDADDR_BREDR);
+       if (device != NULL &&
+               btd_device_is_connected(device) &&
+               g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
+                                               (GCompareFunc)strcasecmp)) {
+               char device_addr[18];
+               ba2str(&device_bdaddr, device_addr);
+               DBG("device %s already known, skipping", device_addr);
+               return false;
        }
 
-       syspath = udev_list_entry_get_name(dev_list_entry);
-
-       /*
-        * All the sysfs paths of the LEDs have the same structure, just the
-        * number changes, so strip it and store only the common prefix.
-        *
-        * Subtracting 1 here means assuming that the LED number is a single
-        * digit, this is safe as the kernel driver only exposes 4 LEDs.
-        */
-       syspath_prefix = strndup(syspath, strlen(syspath) - 1);
-
-out:
-       udev_enumerate_unref(enumerate);
-
-       return syspath_prefix;
-}
-
-static struct leds_data *get_leds_data(struct udev_device *udevice)
-{
-       struct leds_data *data;
-       int number;
+       device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
 
-       number = get_js_number(udevice);
-       DBG("number %d", number);
+       info("sixaxis: setting up new device");
 
-       data = malloc0(sizeof(*data));
-       if (!data)
-               return NULL;
+       btd_device_device_set_name(device, cp->name);
+       btd_device_set_pnpid(device, cp->source, cp->vid, cp->pid, cp->version);
+       btd_device_set_temporary(device, true);
 
-       data->bitmap = calc_leds_bitmap(number);
-       if (data->bitmap == 0) {
-               leds_data_destroy(data);
-               return NULL;
+       closure = g_new0(struct authentication_closure, 1);
+       if (!closure) {
+               btd_adapter_remove_device(adapter, device);
+               return false;
        }
+       closure->adapter = adapter;
+       closure->device = device;
+       closure->sysfs_path = g_strdup(sysfs_path);
+       closure->fd = fd;
+       bacpy(&closure->bdaddr, &device_bdaddr);
+       closure->type = cp->type;
+       adapter_bdaddr = btd_adapter_get_address(adapter);
+       closure->auth_id = btd_request_authorization_cable_configured(
+                                       adapter_bdaddr, &device_bdaddr,
+                                       HID_UUID, agent_auth_cb, closure);
 
-       /*
-        * It's OK if this fails, set_leds_hidraw() will be used in
-        * case data->syspath_prefix is NULL.
-        */
-       data->syspath_prefix = get_leds_syspath_prefix(udevice);
+       g_hash_table_insert(pending_auths, closure->sysfs_path, closure);
 
-       return data;
+       return true;
 }
 
-static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
+static const struct cable_pairing *
+get_pairing_type_for_device(struct udev_device *udevice, uint16_t *bus,
+                                               char **sysfs_path)
 {
        struct udev_device *hid_parent;
-       uint16_t vid, pid;
        const char *hid_id;
-       guint i;
+       const struct cable_pairing *cp;
+       uint16_t vid, pid;
 
        hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
                                                                "hid", NULL);
        if (!hid_parent)
-               return -1;
+               return NULL;
 
        hid_id = udev_device_get_property_value(hid_parent, "HID_ID");
 
        if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
-               return -1;
+               return NULL;
 
-       for (i = 0; i < G_N_ELEMENTS(devices); i++) {
-               if (devices[i].vid == vid && devices[i].pid == pid)
-                       return i;
-       }
+       cp = get_pairing(vid, pid);
+       *sysfs_path = g_strdup(udev_device_get_syspath(udevice));
 
-       return -1;
+       return cp;
 }
 
 static void device_added(struct udev_device *udevice)
 {
        struct btd_adapter *adapter;
-       GIOChannel *io;
        uint16_t bus;
-       int index;
+       char *sysfs_path = NULL;
+       const struct cable_pairing *cp;
        int fd;
 
        adapter = btd_adapter_get_default();
        if (!adapter)
                return;
 
-       index = get_supported_device(udevice, &bus);
-       if (index < 0)
+       cp = get_pairing_type_for_device(udevice, &bus, &sysfs_path);
+       if (!cp || (cp->type != CABLE_PAIRING_SIXAXIS &&
+                               cp->type != CABLE_PAIRING_DS4))
+               return;
+       if (bus != BUS_USB)
                return;
 
-       info("sixaxis: compatible device connected: %s (%04X:%04X)",
-                               devices[index].name, devices[index].vid,
-                               devices[index].pid);
+       info("sixaxis: compatible device connected: %s (%04X:%04X %s)",
+                               cp->name, cp->vid, cp->pid, sysfs_path);
 
        fd = open(udev_device_get_devnode(udevice), O_RDWR);
-       if (fd < 0)
+       if (fd < 0) {
+               g_free(sysfs_path);
                return;
+       }
 
-       io = g_io_channel_unix_new(fd);
+       /* Only close the fd if an authentication is not pending */
+       if (!setup_device(fd, sysfs_path, cp, adapter))
+               close(fd);
 
-       switch (bus) {
-       case BUS_USB:
-               if (!setup_device(fd, index, adapter))
-                       break;
+       g_free(sysfs_path);
+}
 
-               /* fall through */
-       case BUS_BLUETOOTH:
-               /* wait for events before setting leds */
-               g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               setup_leds, get_leds_data(udevice));
+static void device_removed(struct udev_device *udevice)
+{
+       struct authentication_closure *closure;
+       const char *sysfs_path;
 
-               break;
-       default:
-               DBG("uknown bus type (%u)", bus);
-               break;
-       }
+       sysfs_path = udev_device_get_syspath(udevice);
+       if (!sysfs_path)
+               return;
+
+       closure = g_hash_table_lookup(pending_auths, sysfs_path);
+       if (!closure)
+               return;
 
-       g_io_channel_set_close_on_unref(io, TRUE);
-       g_io_channel_unref(io);
+       g_hash_table_steal(pending_auths, sysfs_path);
+       auth_closure_destroy(closure, true);
 }
 
 static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
@@ -536,6 +475,8 @@ static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
 
        if (!g_strcmp0(udev_device_get_action(udevice), "add"))
                device_added(udevice);
+       else if (!g_strcmp0(udev_device_get_action(udevice), "remove"))
+               device_removed(udevice);
 
        udev_device_unref(udevice);
 
@@ -569,13 +510,27 @@ static int sixaxis_init(void)
        watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL);
        g_io_channel_unref(channel);
 
+       pending_auths = g_hash_table_new(g_str_hash,
+                                       g_str_equal);
+
        return 0;
 }
 
 static void sixaxis_exit(void)
 {
+       GHashTableIter iter;
+       gpointer value;
+
        DBG("");
 
+       g_hash_table_iter_init(&iter, pending_auths);
+       while (g_hash_table_iter_next(&iter, NULL, &value)) {
+               struct authentication_closure *closure = value;
+               auth_closure_destroy(closure, true);
+       }
+       g_hash_table_destroy(pending_auths);
+       pending_auths = NULL;
+
        g_source_remove(watch_id);
        watch_id = 0;
 
index 9a1a566..2f6e3cd 100755 (executable)
@@ -21,6 +21,7 @@
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
index c308754..787989d 100644 (file)
@@ -115,6 +115,7 @@ struct a2dp_setup {
        gboolean reconfigure;
        gboolean start;
        GSList *cb;
+       GIOChannel *io;
        int ref;
 };
 
@@ -169,6 +170,11 @@ static void setup_free(struct a2dp_setup *s)
 {
        DBG("%p", s);
 
+       if (s->io) {
+               g_io_channel_shutdown(s->io, TRUE, NULL);
+               g_io_channel_unref(s->io);
+       }
+
        setups = g_slist_remove(setups, s);
        if (s->session)
                avdtp_unref(s->session);
@@ -518,6 +524,7 @@ static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
        }
 
        auto_config(setup);
+       setup_unref(setup);
 }
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -636,12 +643,13 @@ static gboolean endpoint_setconf_ind(struct avdtp *session,
                ret = a2dp_sep->endpoint->set_configuration(a2dp_sep,
                                                codec->data,
                                                cap->length - sizeof(*codec),
-                                               setup,
+                                               setup_ref(setup),
                                                endpoint_setconf_cb,
                                                a2dp_sep->user_data);
                if (ret == 0)
                        return TRUE;
 
+               setup_unref(setup);
                setup->err = g_new(struct avdtp_error, 1);
                avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
                                        AVDTP_UNSUPPORTED_CONFIGURATION);
@@ -707,16 +715,18 @@ static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret)
        if (ret == FALSE) {
                setup->stream = NULL;
                finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
-               return;
+               goto done;
        }
 
        err = avdtp_open(setup->session, setup->stream);
        if (err == 0)
-               return;
+               goto done;
 
        error("Error on avdtp_open %s (%d)", strerror(-err), -err);
        setup->stream = NULL;
        finalize_setup_errno(setup, err, finalize_config, NULL);
+done:
+       setup_unref(setup);
 }
 
 static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
@@ -776,7 +786,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
                err = a2dp_sep->endpoint->set_configuration(a2dp_sep,
                                                codec->data, service->length -
                                                sizeof(*codec),
-                                               setup,
+                                               setup_ref(setup),
                                                endpoint_open_cb,
                                                a2dp_sep->user_data);
                if (err == 0)
@@ -784,6 +794,7 @@ static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
 
                setup->stream = NULL;
                finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+               setup_unref(setup);
                return;
        }
 
@@ -1481,6 +1492,7 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
 {
        struct a2dp_server *server;
        struct a2dp_channel *chan;
+       const struct queue_entry *entry;
 
        server = find_server(servers, device_get_adapter(device));
        if (server == NULL)
@@ -1503,6 +1515,20 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
        if (chan->session)
                return avdtp_ref(chan->session);
 
+       /* Check if there is any SEP available */
+       for (entry = queue_get_entries(server->seps); entry;
+                                       entry = entry->next) {
+               struct avdtp_local_sep *sep = entry->data;
+
+               if (avdtp_sep_get_state(sep) == AVDTP_STATE_IDLE)
+                       goto found;
+       }
+
+       DBG("Unable to find any available SEP");
+
+       return NULL;
+
+found:
        chan->session = avdtp_new(NULL, device, server->seps);
        if (!chan->session) {
                channel_remove(chan);
@@ -1588,6 +1614,9 @@ static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
 
        g_io_channel_set_close_on_unref(io, FALSE);
 
+       g_io_channel_unref(setup->io);
+       setup->io = NULL;
+
        setup_unref(setup);
 
        return;
@@ -1644,12 +1673,24 @@ static void confirm_cb(GIOChannel *io, gpointer data)
                if (!setup || !setup->stream)
                        goto drop;
 
+               if (setup->io) {
+                       error("transport channel already exists");
+                       goto drop;
+               }
+
                if (!bt_io_accept(io, transport_cb, setup, NULL, &err)) {
                        error("bt_io_accept: %s", err->message);
                        g_error_free(err);
                        goto drop;
                }
 
+               /*
+                * Reference the channel so it can be shutdown properly
+                * stopping bt_io_accept from calling the callback with invalid
+                * setup pointer.
+                */
+               setup->io = g_io_channel_ref(io);
+
                return;
        }
 
@@ -1697,12 +1738,12 @@ static bool a2dp_server_listen(struct a2dp_server *server)
        }
 #else
        server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
-                       BT_IO_OPT_SOURCE_BDADDR,
-                       btd_adapter_get_address(server->adapter),
-                       BT_IO_OPT_PSM, AVDTP_PSM,
-                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
-                       BT_IO_OPT_MASTER, true,
-                       BT_IO_OPT_INVALID);
+                               BT_IO_OPT_SOURCE_BDADDR,
+                               btd_adapter_get_address(server->adapter),
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, true,
+                               BT_IO_OPT_INVALID);
 #endif
        if (server->io)
                return true;
@@ -1931,6 +1972,7 @@ static void select_cb(struct a2dp_setup *setup, void *ret, int size)
 
 done:
        finalize_select(setup);
+       setup_unref(setup);
 }
 
 static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
@@ -2093,11 +2135,13 @@ unsigned int a2dp_select_capabilities(struct avdtp *session,
 
        err = sep->endpoint->select_configuration(sep, codec->data,
                                        service->length - sizeof(*codec),
-                                       setup,
+                                       setup_ref(setup),
                                        select_cb, sep->user_data);
        if (err == 0)
                return cb_data->id;
 
+       setup_unref(setup);
+
 fail:
        setup_cb_free(cb_data);
        return 0;
index e286c02..9585d28 100644 (file)
  */
 #define AVC_PRESS_TIMEOUT      2
 
+#define CONTROL_TIMEOUT                10
+#define BROWSING_TIMEOUT       10
+
+#define PASSTHROUGH_QUEUE      0
+#define CONTROL_QUEUE          1
+
 #define QUIRK_NO_RELEASE 1 << 0
 
 /* Message types */
@@ -151,15 +157,23 @@ struct avctp_browsing_req {
 typedef int (*avctp_process_cb) (void *data);
 
 struct avctp_pending_req {
-       struct avctp_channel *chan;
+       struct avctp_queue *queue;
        uint8_t transaction;
        guint timeout;
+       bool retry;
        int err;
        avctp_process_cb process;
        void *data;
        GDestroyNotify destroy;
 };
 
+struct avctp_queue {
+       struct avctp_channel *chan;
+       struct avctp_pending_req *p;
+       GQueue *queue;
+       guint process_id;
+};
+
 struct avctp_channel {
        struct avctp *session;
        GIOChannel *io;
@@ -169,10 +183,8 @@ struct avctp_channel {
        uint16_t omtu;
        uint8_t *buffer;
        GSList *handlers;
-       struct avctp_pending_req *p;
-       GQueue *queue;
+       GSList *queues;
        GSList *processed;
-       guint process_id;
        GDestroyNotify destroy;
 };
 
@@ -536,6 +548,21 @@ static void pending_destroy(gpointer data, gpointer user_data)
        g_free(req);
 }
 
+static void avctp_queue_destroy(void *data)
+{
+       struct avctp_queue *queue = data;
+
+       if (queue->process_id > 0)
+               g_source_remove(queue->process_id);
+
+       if (queue->p)
+               pending_destroy(queue->p, NULL);
+
+       g_queue_foreach(queue->queue, pending_destroy, NULL);
+       g_queue_free(queue->queue);
+       g_free(queue);
+}
+
 static void avctp_channel_destroy(struct avctp_channel *chan)
 {
        g_io_channel_shutdown(chan->io, TRUE, NULL);
@@ -544,18 +571,11 @@ static void avctp_channel_destroy(struct avctp_channel *chan)
        if (chan->watch)
                g_source_remove(chan->watch);
 
-       if (chan->p)
-               pending_destroy(chan->p, NULL);
-
-       if (chan->process_id > 0)
-               g_source_remove(chan->process_id);
-
        if (chan->destroy)
                chan->destroy(chan);
 
        g_free(chan->buffer);
-       g_queue_foreach(chan->queue, pending_destroy, NULL);
-       g_queue_free(chan->queue);
+       g_slist_free_full(chan->queues, avctp_queue_destroy);
        g_slist_foreach(chan->processed, pending_destroy, NULL);
        g_slist_free(chan->processed);
        g_slist_free_full(chan->handlers, g_free);
@@ -641,6 +661,39 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state,
        }
 }
 
+static uint8_t chan_get_transaction(struct avctp_channel *chan)
+{
+       GSList *l, *tmp;
+       uint8_t transaction;
+
+       if (!chan->processed)
+               goto done;
+
+       tmp = g_slist_copy(chan->processed);
+
+       /* Find first unused transaction id */
+       for (l = tmp; l; l = g_slist_next(l)) {
+               struct avctp_pending_req *req = l->data;
+
+               if (req->transaction == chan->transaction) {
+                       chan->transaction++;
+                       chan->transaction %= 16;
+                       tmp = g_slist_delete_link(tmp, l);
+                       l = tmp;
+               }
+       }
+
+       g_slist_free(tmp);
+
+done:
+       transaction = chan->transaction;
+
+       chan->transaction++;
+       chan->transaction %= 16;
+
+       return transaction;
+}
+
 static int avctp_send(struct avctp_channel *control, uint8_t transaction,
                                uint8_t cr, uint8_t code,
                                uint8_t subunit, uint8_t opcode,
@@ -667,6 +720,9 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
        avctp = (void *) control->buffer;
        avc = (void *) avctp + sizeof(*avctp);
 
+       if (transaction > 16)
+               transaction = chan_get_transaction(control);
+
        avctp->transaction = transaction;
        avctp->packet_type = AVCTP_PACKET_SINGLE;
        avctp->cr = cr;
@@ -686,10 +742,11 @@ static int avctp_send(struct avctp_channel *control, uint8_t transaction,
        return err;
 }
 
-static int avctp_browsing_send(struct avctp_channel *browsing,
+static int avctp_browsing_send(struct avctp_queue *queue,
                                uint8_t transaction, uint8_t cr,
                                uint8_t *operands, size_t operand_count)
 {
+       struct avctp_channel *browsing = queue->chan;
        struct avctp_header *avctp;
        struct msghdr msg;
        struct iovec iov[2];
@@ -728,7 +785,7 @@ static void control_req_destroy(void *data)
 {
        struct avctp_control_req *req = data;
        struct avctp_pending_req *p = req->p;
-       struct avctp *session = p->chan->session;
+       struct avctp *session = p->queue->chan->session;
 
        if (p->err == 0 || req->func == NULL)
                goto done;
@@ -745,7 +802,7 @@ static void browsing_req_destroy(void *data)
 {
        struct avctp_browsing_req *req = data;
        struct avctp_pending_req *p = req->p;
-       struct avctp *session = p->chan->session;
+       struct avctp *session = p->queue->chan->session;
 
        if (p->err == 0 || req->func == NULL)
                goto done;
@@ -759,53 +816,124 @@ done:
 
 static gboolean req_timeout(gpointer user_data)
 {
-       struct avctp_channel *chan = user_data;
-       struct avctp_pending_req *p = chan->p;
+       struct avctp_queue *queue = user_data;
+       struct avctp_pending_req *p = queue->p;
 
-       DBG("transaction %u", p->transaction);
+       DBG("transaction %u retry %s", p->transaction, p->retry ? "true" :
+                                                               "false");
 
        p->timeout = 0;
+
+       if (p->retry) {
+               p->process(p->data);
+               return FALSE;
+       }
+
        p->err = -ETIMEDOUT;
 
        pending_destroy(p, NULL);
-       chan->p = NULL;
+       queue->p = NULL;
 
-       if (chan->process_id == 0)
-               chan->process_id = g_idle_add(process_queue, chan);
+       if (queue->process_id == 0)
+               queue->process_id = g_idle_add(process_queue, queue);
 
        return FALSE;
 }
 
+static int process_passthrough(void *data)
+{
+       struct avctp_control_req *req = data;
+       struct avctp_pending_req *p = req->p;
+       int ret;
+
+       ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+                       req->code, req->subunit, req->op, req->operands,
+                       req->operand_count);
+       if (ret < 0)
+               return ret;
+
+       p->timeout = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, req_timeout,
+                                                               p->queue);
+
+       return 0;
+}
+
 static int process_control(void *data)
 {
        struct avctp_control_req *req = data;
        struct avctp_pending_req *p = req->p;
+       int ret;
 
-       return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
-                                       req->subunit, req->op,
-                                       req->operands, req->operand_count);
+       ret = avctp_send(p->queue->chan, p->transaction, AVCTP_COMMAND,
+                       req->code, req->subunit, req->op, req->operands,
+                       req->operand_count);
+       if (ret < 0)
+               return ret;
+
+       p->retry = !p->retry;
+
+       p->timeout = g_timeout_add_seconds(CONTROL_TIMEOUT, req_timeout,
+                                                               p->queue);
+
+       return 0;
 }
 
 static int process_browsing(void *data)
 {
        struct avctp_browsing_req *req = data;
        struct avctp_pending_req *p = req->p;
+       int ret;
 
-       return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+       ret = avctp_browsing_send(p->queue, p->transaction, AVCTP_COMMAND,
                                        req->operands, req->operand_count);
+       if (ret < 0)
+               return ret;
+
+       p->timeout = g_timeout_add_seconds(BROWSING_TIMEOUT, req_timeout,
+                                                               p->queue);
+
+       return 0;
+}
+
+static gboolean process_queue(void *user_data)
+{
+        struct avctp_queue *queue = user_data;
+        struct avctp_pending_req *p = queue->p;
+
+        queue->process_id = 0;
+
+        if (p != NULL)
+                return FALSE;
+
+        while ((p = g_queue_pop_head(queue->queue))) {
+
+                if (p->process(p->data) == 0)
+                        break;
+
+                pending_destroy(p, NULL);
+        }
+
+        if (p == NULL)
+                return FALSE;
+
+        queue->p = p;
+
+        return FALSE;
+
 }
 
+#if 0
 static gboolean process_queue(void *user_data)
 {
-       struct avctp_channel *chan = user_data;
-       struct avctp_pending_req *p = chan->p;
+       struct avctp_queue *queue = user_data;
+       struct avctp_pending_req *p = queue->p;
 
-       chan->process_id = 0;
+       queue->process_id = 0;
 
        if (p != NULL)
                return FALSE;
 
-       while ((p = g_queue_pop_head(chan->queue))) {
+       while ((p = g_queue_pop_head(queue->queue))) {
 
                if (p->process(p->data) == 0)
                        break;
@@ -816,16 +944,15 @@ static gboolean process_queue(void *user_data)
        if (p == NULL)
                return FALSE;
 
-       chan->p = p;
+       queue->p = p;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        p->timeout = g_timeout_add_seconds(5, req_timeout, chan);
-#else
-       p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
 #endif
 
        return FALSE;
 
 }
+#endif
 
 static void control_response(struct avctp_channel *control,
                                        struct avctp_header *avctp,
@@ -833,11 +960,23 @@ static void control_response(struct avctp_channel *control,
                                        uint8_t *operands,
                                        size_t operand_count)
 {
-       struct avctp_pending_req *p = control->p;
+       struct avctp_pending_req *p;
        struct avctp_control_req *req;
+       struct avctp_queue *queue;
        GSList *l;
 
+       if (avc->opcode == AVC_OP_PASSTHROUGH)
+               queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+       else
+               queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+
+       p = queue->p;
+
        if (p && p->transaction == avctp->transaction) {
+               req = p->data;
+               if (req->op != avc->opcode)
+                       goto done;
+
                control->processed = g_slist_prepend(control->processed, p);
 
                if (p->timeout > 0) {
@@ -845,13 +984,13 @@ static void control_response(struct avctp_channel *control,
                        p->timeout = 0;
                }
 
-               control->p = NULL;
+               queue->p = NULL;
 
-               if (control->process_id == 0)
-                       control->process_id = g_idle_add(process_queue,
-                                                               control);
+               if (queue->process_id == 0)
+                       queue->process_id = g_idle_add(process_queue, queue);
        }
 
+done:
        for (l = control->processed; l; l = l->next) {
                p = l->data;
                req = p->data;
@@ -859,6 +998,9 @@ static void control_response(struct avctp_channel *control,
                if (p->transaction != avctp->transaction)
                        continue;
 
+               if (req->op != avc->opcode)
+                       continue;
+
                if (req->func && req->func(control->session, avc->code,
                                        avc->subunit_type, p->transaction,
                                        operands, operand_count,
@@ -877,10 +1019,15 @@ static void browsing_response(struct avctp_channel *browsing,
                                        uint8_t *operands,
                                        size_t operand_count)
 {
-       struct avctp_pending_req *p = browsing->p;
+       struct avctp_pending_req *p;
        struct avctp_browsing_req *req;
+       struct avctp_queue *queue;
        GSList *l;
 
+       queue = g_slist_nth_data(browsing->queues, 0);
+
+       p = queue->p;
+
        if (p && p->transaction == avctp->transaction) {
                browsing->processed = g_slist_prepend(browsing->processed, p);
 
@@ -889,11 +1036,10 @@ static void browsing_response(struct avctp_channel *browsing,
                        p->timeout = 0;
                }
 
-               browsing->p = NULL;
+               queue->p = NULL;
 
-               if (browsing->process_id == 0)
-                       browsing->process_id = g_idle_add(process_queue,
-                                                               browsing);
+               if (queue->process_id == 0)
+                       queue->process_id = g_idle_add(process_queue, queue);
        }
 
        for (l = browsing->processed; l; l = l->next) {
@@ -1153,8 +1299,20 @@ static void init_uinput(struct avctp *session)
                DBG("AVRCP: uinput initialized for %s", address);
 }
 
+static struct avctp_queue *avctp_queue_create(struct avctp_channel *chan)
+{
+       struct avctp_queue *queue;
+
+       queue = g_new0(struct avctp_queue, 1);
+       queue->chan = chan;
+       queue->queue = g_queue_new();
+
+       return queue;
+}
+
 static struct avctp_channel *avctp_channel_create(struct avctp *session,
                                                        GIOChannel *io,
+                                                       int queues,
                                                        GDestroyNotify destroy)
 {
        struct avctp_channel *chan;
@@ -1162,9 +1320,15 @@ static struct avctp_channel *avctp_channel_create(struct avctp *session,
        chan = g_new0(struct avctp_channel, 1);
        chan->session = session;
        chan->io = g_io_channel_ref(io);
-       chan->queue = g_queue_new();
        chan->destroy = destroy;
 
+       while (queues--) {
+               struct avctp_queue *queue;
+
+               queue = avctp_queue_create(chan);
+               chan->queues = g_slist_prepend(chan->queues, queue);
+       }
+
        return chan;
 }
 
@@ -1192,6 +1356,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
 {
        struct avctp *session = data;
        struct avctp_channel *browsing = session->browsing;
+       struct avctp_queue *queue;
        char address[18];
        uint16_t imtu, omtu;
        GError *gerr = NULL;
@@ -1217,7 +1382,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
        DBG("AVCTP Browsing: connected to %s", address);
 
        if (browsing == NULL) {
-               browsing = avctp_channel_create(session, chan,
+               browsing = avctp_channel_create(session, chan, 1,
                                                avctp_destroy_browsing);
                session->browsing = browsing;
        }
@@ -1232,8 +1397,9 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
        avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0);
 
        /* Process any request that was pending the connection to complete */
-       if (browsing->process_id == 0 && !g_queue_is_empty(browsing->queue))
-               browsing->process_id = g_idle_add(process_queue, browsing);
+       queue = g_slist_nth_data(browsing->queues, 0);
+       if (queue->process_id == 0 && !g_queue_is_empty(queue->queue))
+               queue->process_id = g_idle_add(process_queue, queue);
 
        return;
 
@@ -1274,7 +1440,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
        DBG("AVCTP: connected to %s", address);
 
        if (session->control == NULL)
-               session->control = avctp_channel_create(session, chan, NULL);
+               session->control = avctp_channel_create(session, chan, 2, NULL);
 
        session->control->imtu = imtu;
        session->control->omtu = omtu;
@@ -1396,7 +1562,7 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
        }
 
        avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
-       session->control = avctp_channel_create(session, chan, NULL);
+       session->control = avctp_channel_create(session, chan, 2, NULL);
 
        src = btd_adapter_get_address(device_get_adapter(dev));
        dst = device_get_address(dev);
@@ -1586,44 +1752,20 @@ void avctp_unregister(struct btd_adapter *adapter)
        g_free(server);
 }
 
-static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+static struct avctp_pending_req *pending_create(struct avctp_queue *queue,
                                                avctp_process_cb process,
                                                void *data,
                                                GDestroyNotify destroy)
 {
        struct avctp_pending_req *p;
-       GSList *l, *tmp;
-
-       if (!chan->processed)
-               goto done;
-
-       tmp = g_slist_copy(chan->processed);
-
-       /* Find first unused transaction id */
-       for (l = tmp; l; l = g_slist_next(l)) {
-               struct avctp_pending_req *req = l->data;
-
-               if (req->transaction == chan->transaction) {
-                       chan->transaction++;
-                       chan->transaction %= 16;
-                       tmp = g_slist_delete_link(tmp, l);
-                       l = tmp;
-               }
-       }
-
-       g_slist_free(tmp);
 
-done:
        p = g_new0(struct avctp_pending_req, 1);
-       p->chan = chan;
-       p->transaction = chan->transaction;
+       p->queue = queue;
+       p->transaction = chan_get_transaction(queue->chan);
        p->process = process;
        p->data = data;
        p->destroy = destroy;
 
-       chan->transaction++;
-       chan->transaction %= 16;
-
        return p;
 }
 
@@ -1633,12 +1775,18 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
                                avctp_rsp_cb func, void *user_data)
 {
        struct avctp_channel *control = session->control;
+       struct avctp_queue *queue;
        struct avctp_pending_req *p;
        struct avctp_control_req *req;
 
        if (control == NULL)
                return -ENOTCONN;
 
+       /* If the request set a callback send it directly */
+       if (!func)
+               return avctp_send(session->control, -1, AVCTP_COMMAND,
+                               code, subunit, opcode, operands, operand_count);
+
        req = g_new0(struct avctp_control_req, 1);
        req->code = code;
        req->subunit = subunit;
@@ -1648,14 +1796,22 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
        req->operand_count = operand_count;
        req->user_data = user_data;
 
-       p = pending_create(control, process_control, req, control_req_destroy);
+       if (opcode == AVC_OP_PASSTHROUGH) {
+               queue = g_slist_nth_data(control->queues, PASSTHROUGH_QUEUE);
+               p = pending_create(queue, process_passthrough, req,
+                                       control_req_destroy);
+       } else {
+               queue = g_slist_nth_data(control->queues, CONTROL_QUEUE);
+               p = pending_create(queue, process_control, req,
+                                       control_req_destroy);
+       }
 
        req->p = p;
 
-       g_queue_push_tail(control->queue, p);
+       g_queue_push_tail(queue->queue, p);
 
-       if (control->process_id == 0)
-               control->process_id = g_idle_add(process_queue, control);
+       if (queue->process_id == 0)
+               queue->process_id = g_idle_add(process_queue, queue);
 
        return 0;
 }
@@ -1665,6 +1821,7 @@ int avctp_send_browsing_req(struct avctp *session,
                                avctp_browsing_rsp_cb func, void *user_data)
 {
        struct avctp_channel *browsing = session->browsing;
+       struct avctp_queue *queue;
        struct avctp_pending_req *p;
        struct avctp_browsing_req *req;
 
@@ -1677,19 +1834,20 @@ int avctp_send_browsing_req(struct avctp *session,
        req->operand_count = operand_count;
        req->user_data = user_data;
 
-       p = pending_create(browsing, process_browsing, req,
-                       browsing_req_destroy);
+       queue = g_slist_nth_data(browsing->queues, 0);
+
+       p = pending_create(queue, process_browsing, req, browsing_req_destroy);
 
        req->p = p;
 
-       g_queue_push_tail(browsing->queue, p);
+       g_queue_push_tail(queue->queue, p);
 
        /* Connection did not complete, delay process of the request */
        if (browsing->watch == 0)
                return 0;
 
-       if (browsing->process_id == 0)
-               browsing->process_id = g_idle_add(process_queue, browsing);
+       if (queue->process_id == 0)
+               queue->process_id = g_idle_add(process_queue, queue);
 
        return 0;
 }
@@ -2081,7 +2239,7 @@ struct avctp *avctp_connect(struct btd_device *device)
                return NULL;
        }
 
-       session->control = avctp_channel_create(session, io, NULL);
+       session->control = avctp_channel_create(session, io, 2, NULL);
        session->initiator = true;
        g_io_channel_unref(io);
 
@@ -2118,7 +2276,7 @@ int avctp_connect_browsing(struct avctp *session)
                return -EIO;
        }
 
-       session->browsing = avctp_channel_create(session, io,
+       session->browsing = avctp_channel_create(session, io, 1,
                                                avctp_destroy_browsing);
        g_io_channel_unref(io);
 
index 4bc42ac..f60cb02 100644 (file)
@@ -100,6 +100,7 @@ static unsigned int seids;
 #else
 #define REQ_TIMEOUT 6
 #endif
+#define SUSPEND_TIMEOUT 10
 #define ABORT_TIMEOUT 2
 #define DISCONNECT_TIMEOUT 1
 #define START_TIMEOUT 1
@@ -1217,6 +1218,7 @@ static void avdtp_sep_set_state(struct avdtp *session,
                        handle_unanswered_req(session, stream);
                /* Remove pending commands for this stream from the queue */
                cleanup_queue(session, stream);
+               session->streams = g_slist_remove(session->streams, stream);
                break;
        default:
                break;
@@ -1229,11 +1231,8 @@ static void avdtp_sep_set_state(struct avdtp *session,
                cb->cb(stream, old_state, state, err_ptr, cb->user_data);
        }
 
-       if (state == AVDTP_STATE_IDLE &&
-                               g_slist_find(session->streams, stream)) {
-               session->streams = g_slist_remove(session->streams, stream);
+       if (state == AVDTP_STATE_IDLE)
                stream_free(stream);
-       }
 }
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -1477,6 +1476,13 @@ static void set_disconnect_timer(struct avdtp *session)
                                disconnect_timeout,
                                session);
        }
+#else
+       if (!session->stream_setup && !session->streams)
+               session->dc_timer = g_idle_add(disconnect_timeout, session);
+       else
+               session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+                                                       disconnect_timeout,
+                                                       session);
 #endif
 }
 
@@ -2775,13 +2781,13 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
        }
 #else
        io = bt_io_connect(avdtp_connect_cb, session,
-                       NULL, &err,
-                       BT_IO_OPT_SOURCE_BDADDR, src,
-                       BT_IO_OPT_DEST_BDADDR,
-                       device_get_address(session->device),
-                       BT_IO_OPT_PSM, AVDTP_PSM,
-                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
-                       BT_IO_OPT_INVALID);
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_DEST_BDADDR,
+                               device_get_address(session->device),
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
 #endif
 
        if (!io) {
@@ -2830,10 +2836,9 @@ static int cancel_request(struct avdtp *session, int err)
        else
                stream = NULL;
 
-       if (stream) {
-               stream->abort_int = TRUE;
+       if (stream)
                lsep = stream->lsep;
-       else
+       else
                lsep = NULL;
 
        switch (req->signal_id) {
@@ -2878,7 +2883,7 @@ static int cancel_request(struct avdtp *session, int err)
                if (lsep && lsep->cfm && lsep->cfm->set_configuration)
                        lsep->cfm->set_configuration(session, lsep, stream,
                                                        &averr, lsep->user_data);
-               goto failed;
+               break;
        case AVDTP_DISCOVER:
                error("Discover: %s (%d)", strerror(err), err);
                goto failed;
@@ -2903,6 +2908,8 @@ static int cancel_request(struct avdtp *session, int err)
                goto failed;
        }
 
+       stream->abort_int = TRUE;
+
        goto done;
 
 failed:
@@ -2925,7 +2932,7 @@ static int send_req(struct avdtp *session, gboolean priority,
                        struct pending_req *req)
 {
        static int transaction = 0;
-       int err;
+       int err, timeout;
 
        if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
                session->io = l2cap_connect(session);
@@ -2955,10 +2962,18 @@ static int send_req(struct avdtp *session, gboolean priority,
 
        session->req = req;
 
-       req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
-                                       ABORT_TIMEOUT : REQ_TIMEOUT,
-                                       request_timeout,
-                                       session);
+       switch (req->signal_id) {
+       case AVDTP_ABORT:
+               timeout = ABORT_TIMEOUT;
+               break;
+       case AVDTP_SUSPEND:
+               timeout = SUSPEND_TIMEOUT;
+               break;
+       default:
+               timeout = REQ_TIMEOUT;
+       }
+
+       req->timeout = g_timeout_add_seconds(timeout, request_timeout, session);
        return 0;
 
 failed:
@@ -3220,6 +3235,7 @@ static gboolean avdtp_parse_resp(struct avdtp *session,
                return avdtp_discover_resp(session, buf, size);
        case AVDTP_GET_ALL_CAPABILITIES:
                get_all = "ALL_";
+               /* fall through */
        case AVDTP_GET_CAPABILITIES:
                DBG("GET_%sCAPABILITIES request succeeded", get_all);
                if (!avdtp_get_capabilities_resp(session, buf, size))
index 712a553..5fea44c 100644 (file)
@@ -249,6 +249,7 @@ struct avrcp_player {
 
        struct pending_list_items *p;
        char *change_path;
+       uint64_t change_uid;
 
        struct avrcp_player_cb *cb;
        void *user_data;
@@ -2755,11 +2756,8 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
                else
                        item = parse_media_folder(session, &operands[i], len);
 
-               if (item) {
-                       if (g_slist_find(p->items, item))
-                               goto done;
+               if (item)
                        p->items = g_slist_append(p->items, item);
-               }
 
                i += len;
        }
@@ -2860,7 +2858,10 @@ done:
                player->change_path = NULL;
        }
 
-       media_player_change_folder_complete(mp, player->path, ret);
+       media_player_change_folder_complete(mp, player->path,
+                                               player->change_uid, ret);
+
+       player->change_uid = 0;
 
        return FALSE;
 }
@@ -3372,6 +3373,7 @@ static int ct_change_folder(struct media_player *mp, const char *path,
        session = player->sessions->data;
        set_ct_player(session, player);
        player->change_path = g_strdup(path);
+       player->change_uid = uid;
 
        direction = g_str_has_prefix(path, player->path) ? 0x01 : 0x00;
 
@@ -3454,7 +3456,7 @@ static gboolean avrcp_play_item_rsp(struct avctp *conn, uint8_t code,
                                        uint8_t *operands, size_t operand_count,
                                        void *user_data)
 {
-       struct avrcp_browsing_header *pdu = (void *) operands;
+       struct avrcp_header *pdu = (void *) operands;
        struct avrcp *session = (void *) user_data;
        struct avrcp_player *player = session->controller->player;
        struct media_player *mp = player->user_data;
@@ -3470,8 +3472,10 @@ static gboolean avrcp_play_item_rsp(struct avctp *conn, uint8_t code,
                case AVRCP_STATUS_UID_CHANGED:
                case AVRCP_STATUS_DOES_NOT_EXIST:
                        ret = -ENOENT;
+                       break;
                default:
                        ret = -EINVAL;
+                       break;
                }
                goto done;
        }
@@ -4246,6 +4250,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
                        if (!session->controller ||
                                                !session->controller->player)
                                break;
+                       /* fall through */
                case AVRCP_EVENT_VOLUME_CHANGED:
 #endif
                        avrcp_register_notification(session, event);
index edc4a98..4ab1f9b 100755 (executable)
@@ -127,7 +127,7 @@ int control_disconnect(struct btd_service *service)
 {
        struct control *control = btd_service_get_user_data(service);
 
-       if (!control->session)
+       if (!control || !control->session)
                return -ENOTCONN;
 
        avctp_disconnect(control->session);
@@ -275,11 +275,15 @@ static void path_unregister(void *data)
 
        avctp_remove_state_cb(control->avctp_id);
 
-       if (control->target)
+       if (control->target) {
+               btd_service_set_user_data(control->target, NULL);
                btd_service_unref(control->target);
+       }
 
-       if (control->remote)
+       if (control->remote) {
+               btd_service_set_user_data(control->remote, NULL);
                btd_service_unref(control->remote);
+       }
 
        devices = g_slist_remove(devices, control);
        g_free(control);
index 8d7bab9..46d95d2 100755 (executable)
@@ -702,30 +702,6 @@ static void parse_folder_list(gpointer data, gpointer user_data)
        dbus_message_iter_close_container(array, &entry);
 }
 
-void media_player_change_folder_complete(struct media_player *mp,
-                                               const char *path, int ret)
-{
-       struct media_folder *folder = mp->scope;
-       DBusMessage *reply;
-
-       if (folder == NULL || folder->msg == NULL)
-               return;
-
-       if (ret < 0) {
-               reply = btd_error_failed(folder->msg, strerror(-ret));
-               goto done;
-       }
-
-       media_player_set_folder(mp, path, ret);
-
-       reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
-
-done:
-       g_dbus_send_message(btd_get_dbus_connection(), reply);
-       dbus_message_unref(folder->msg);
-       folder->msg = NULL;
-}
-
 void media_player_list_complete(struct media_player *mp, GSList *items,
                                                                int err)
 {
@@ -1251,6 +1227,69 @@ static void media_player_set_scope(struct media_player *mp,
        return media_player_change_scope(mp, folder);
 }
 
+static struct media_folder *
+media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
+{
+       struct media_folder *folder = mp->scope;
+       struct media_folder *parent = folder->parent;
+       GSList *l;
+
+       if (parent && parent->item->uid == uid)
+               return parent;
+
+       for (l = folder->subfolders; l; l = l->next) {
+               struct media_folder *folder = l->data;
+
+               if (folder->item->uid == uid)
+                       return folder;
+       }
+
+       return NULL;
+}
+
+static void media_player_set_folder_by_uid(struct media_player *mp,
+                                       uint64_t uid, uint32_t number_of_items)
+{
+       struct media_folder *folder;
+
+       DBG("uid %" PRIu64 " number of items %u", uid, number_of_items);
+
+       folder = media_player_find_folder_by_uid(mp, uid);
+       if (folder == NULL) {
+               error("Unknown folder: %" PRIu64, uid);
+               return;
+       }
+
+       folder->number_of_items = number_of_items;
+
+       media_player_set_scope(mp, folder);
+}
+
+void media_player_change_folder_complete(struct media_player *mp,
+                                               const char *path, uint64_t uid,
+                                               int ret)
+{
+       struct media_folder *folder = mp->scope;
+       DBusMessage *reply;
+
+       if (folder == NULL || folder->msg == NULL)
+               return;
+
+       if (ret < 0) {
+               reply = btd_error_failed(folder->msg, strerror(-ret));
+               goto done;
+       }
+
+       media_player_set_folder_by_uid(mp, uid, ret);
+
+       reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
+
+done:
+       g_dbus_send_message(btd_get_dbus_connection(), reply);
+       dbus_message_unref(folder->msg);
+       folder->msg = NULL;
+}
+
 void media_player_destroy(struct media_player *mp)
 {
        DBG("%s", mp->path);
@@ -1833,7 +1872,7 @@ static gboolean get_metadata(const GDBusPropertyTable *property,
 }
 
 static const GDBusMethodTable media_item_methods[] = {
-       { GDBUS_METHOD("Play", NULL, NULL, media_item_play) },
+       { GDBUS_ASYNC_METHOD("Play", NULL, NULL, media_item_play) },
        { GDBUS_METHOD("AddtoNowPlaying", NULL, NULL,
                                        media_item_add_to_nowplaying) },
        { }
@@ -1944,6 +1983,7 @@ struct media_item *media_player_create_item(struct media_player *mp,
        return media_folder_create_item(mp, mp->scope, name, type, uid);
 }
 
+#if 0
 static struct media_folder *
 media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
 {
@@ -1959,6 +1999,7 @@ media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
 
        return NULL;
 }
+#endif
 
 struct media_item *media_player_create_folder(struct media_player *mp,
                                                const char *name,
index bc3e943..61d04ae 100755 (executable)
@@ -116,7 +116,8 @@ void media_item_set_playable(struct media_item *item, bool value);
 void media_player_list_complete(struct media_player *mp, GSList *items,
                                                                int err);
 void media_player_change_folder_complete(struct media_player *player,
-                                               const char *path, int ret);
+                                               const char *path, uint64_t uid,
+                                               int ret);
 void media_player_search_complete(struct media_player *mp, int ret);
 void media_player_total_items_complete(struct media_player *mp,
                                                uint32_t num_of_items);
diff --git a/profiles/battery/battery.c b/profiles/battery/battery.c
new file mode 100644 (file)
index 0000000..4da4355
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2014  Google Inc.
+ *  Copyright (C) 2017  Red Hat Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/log.h"
+#include "attrib/att.h"
+
+#define BATTERY_INTERFACE "org.bluez.Battery1"
+
+#define BATT_UUID16 0x180f
+
+/* Generic Attribute/Access Service */
+struct batt {
+       char *path; /* D-Bus path of device */
+       struct btd_device *device;
+       struct gatt_db *db;
+       struct bt_gatt_client *client;
+       struct gatt_db_attribute *attr;
+
+       unsigned int batt_level_cb_id;
+       uint16_t batt_level_io_handle;
+
+       uint8_t *initial_value;
+       uint8_t percentage;
+};
+
+static void batt_free(struct batt *batt)
+{
+       gatt_db_unref(batt->db);
+       bt_gatt_client_unref(batt->client);
+       btd_device_unref(batt->device);
+       g_free (batt->initial_value);
+       g_free(batt);
+}
+
+static void batt_reset(struct batt *batt)
+{
+       batt->attr = NULL;
+       gatt_db_unref(batt->db);
+       batt->db = NULL;
+       bt_gatt_client_unref(batt->client);
+       batt->client = NULL;
+       g_free (batt->initial_value);
+       batt->initial_value = NULL;
+       if (batt->path) {
+               g_dbus_unregister_interface(btd_get_dbus_connection(),
+                                           batt->path, BATTERY_INTERFACE);
+               g_free(batt->path);
+               batt->path = NULL;
+       }
+}
+
+static void parse_battery_level(struct batt *batt,
+                               const uint8_t *value)
+{
+       uint8_t percentage;
+
+       percentage = value[0];
+       if (batt->percentage != percentage) {
+               batt->percentage = percentage;
+               DBG("Battery Level updated: %d%%", percentage);
+               g_dbus_emit_property_changed(btd_get_dbus_connection(), batt->path,
+                                               BATTERY_INTERFACE, "Percentage");
+       }
+}
+
+static void batt_io_value_cb(uint16_t value_handle, const uint8_t *value,
+                             uint16_t length, void *user_data)
+{
+       struct batt *batt = user_data;
+
+       if (value_handle == batt->batt_level_io_handle) {
+               parse_battery_level(batt, value);
+       } else {
+               g_assert_not_reached();
+       }
+}
+
+static gboolean property_get_percentage(
+                                       const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct batt *batt = data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &batt->percentage);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable battery_properties[] = {
+       { "Percentage", "y", property_get_percentage },
+       { }
+};
+
+static void batt_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+       struct batt *batt = user_data;
+
+       if (att_ecode != 0) {
+               error("Battery Level: notifications not enabled %s",
+                     att_ecode2str(att_ecode));
+               return;
+       }
+
+       if (g_dbus_register_interface(btd_get_dbus_connection(),
+                                       batt->path, BATTERY_INTERFACE,
+                                       NULL, NULL,
+                                       battery_properties, batt,
+                                       NULL) == FALSE) {
+               error("Unable to register %s interface for %s",
+                       BATTERY_INTERFACE, batt->path);
+               batt_reset(batt);
+               return;
+       }
+
+       parse_battery_level(batt, batt->initial_value);
+       g_free (batt->initial_value);
+       batt->initial_value = NULL;
+
+       DBG("Battery Level: notification enabled");
+}
+
+static void read_initial_battery_level_cb(bool success,
+                                               uint8_t att_ecode,
+                                               const uint8_t *value,
+                                               uint16_t length,
+                                               void *user_data)
+{
+       struct batt *batt = user_data;
+
+       if (!success) {
+               DBG("Reading battery level failed with ATT errror: %u",
+                                                               att_ecode);
+               return;
+       }
+
+       if (!length)
+               return;
+
+       batt->initial_value = g_memdup(value, length);
+
+       /* request notify */
+       batt->batt_level_cb_id =
+               bt_gatt_client_register_notify(batt->client,
+                                              batt->batt_level_io_handle,
+                                              batt_io_ccc_written_cb,
+                                              batt_io_value_cb,
+                                              batt,
+                                              NULL);
+}
+
+static void handle_battery_level(struct batt *batt, uint16_t value_handle)
+{
+       batt->batt_level_io_handle = value_handle;
+
+       if (!bt_gatt_client_read_value(batt->client, batt->batt_level_io_handle,
+                                               read_initial_battery_level_cb, batt, NULL))
+               DBG("Failed to send request to read battery level");
+}
+
+static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
+{
+       bt_uuid_t lhs;
+
+       bt_uuid16_create(&lhs, u16);
+
+       return bt_uuid_cmp(&lhs, uuid) == 0;
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+                                                               void *user_data)
+{
+       struct batt *batt = user_data;
+       uint16_t value_handle;
+       bt_uuid_t uuid;
+
+       if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+                                                               NULL, &uuid)) {
+               error("Failed to obtain characteristic data");
+               return;
+       }
+
+       if (uuid_cmp(GATT_CHARAC_BATTERY_LEVEL, &uuid)) {
+               handle_battery_level(batt, value_handle);
+       } else {
+               char uuid_str[MAX_LEN_UUID_STR];
+
+               bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+               DBG("Unsupported characteristic: %s", uuid_str);
+       }
+}
+
+static void handle_batt_service(struct batt *batt)
+{
+       gatt_db_service_foreach_char(batt->attr, handle_characteristic, batt);
+}
+
+static int batt_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct batt *batt = btd_service_get_user_data(service);
+       char addr[18];
+
+       ba2str(device_get_address(device), addr);
+       DBG("BATT profile probe (%s)", addr);
+
+       /* Ignore, if we were probed for this device already */
+       if (batt) {
+               error("Profile probed twice for the same device!");
+               return -1;
+       }
+
+       batt = g_new0(struct batt, 1);
+       if (!batt)
+               return -1;
+
+       batt->percentage = -1;
+       batt->device = btd_device_ref(device);
+       btd_service_set_user_data(service, batt);
+
+       return 0;
+}
+
+static void batt_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct batt *batt;
+       char addr[18];
+
+       ba2str(device_get_address(device), addr);
+       DBG("BATT profile remove (%s)", addr);
+
+       batt = btd_service_get_user_data(service);
+       if (!batt) {
+               error("BATT service not handled by profile");
+               return;
+       }
+
+       batt_free(batt);
+}
+
+static void foreach_batt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct batt *batt = user_data;
+
+       if (batt->attr) {
+               error("More than one BATT service exists for this device");
+               return;
+       }
+
+       batt->attr = attr;
+       handle_batt_service(batt);
+}
+
+static int batt_accept(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_db *db = btd_device_get_gatt_db(device);
+       struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+       struct batt *batt = btd_service_get_user_data(service);
+       char addr[18];
+       bt_uuid_t batt_uuid;
+
+       ba2str(device_get_address(device), addr);
+       DBG("BATT profile accept (%s)", addr);
+
+       if (!batt) {
+               error("BATT service not handled by profile");
+               return -1;
+       }
+
+       batt->db = gatt_db_ref(db);
+       batt->client = bt_gatt_client_clone(client);
+
+       /* Handle the BATT services */
+       bt_uuid16_create(&batt_uuid, BATT_UUID16);
+       gatt_db_foreach_service(db, &batt_uuid, foreach_batt_service, batt);
+
+       if (!batt->attr) {
+               error("BATT attribute not found");
+               batt_reset(batt);
+               return -1;
+       }
+
+       batt->path = g_strdup (device_get_path(device));
+
+       btd_service_connecting_complete(service, 0);
+
+       return 0;
+}
+
+static int batt_disconnect(struct btd_service *service)
+{
+       struct batt *batt = btd_service_get_user_data(service);
+
+       batt_reset(batt);
+
+       btd_service_disconnecting_complete(service, 0);
+
+       return 0;
+}
+
+static struct btd_profile batt_profile = {
+       .name           = "batt-profile",
+       .remote_uuid    = BATTERY_UUID,
+       .device_probe   = batt_probe,
+       .device_remove  = batt_remove,
+       .accept         = batt_accept,
+       .disconnect     = batt_disconnect,
+};
+
+static int batt_init(void)
+{
+       return btd_profile_register(&batt_profile);
+}
+
+static void batt_exit(void)
+{
+       btd_profile_unregister(&batt_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(battery, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                                       batt_init, batt_exit)
index 91c5d39..6126a77 100755 (executable)
@@ -37,6 +37,8 @@
 
 #include "src/shared/util.h"
 #include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
 
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
@@ -44,6 +46,7 @@
 
 #include "profiles/deviceinfo/dis.h"
 
+#define DIS_UUID16     0x180a
 #define PNP_ID_SIZE    7
 
 struct bt_dis {
@@ -87,7 +90,62 @@ static void dis_free(struct bt_dis *dis)
        g_free(dis);
 }
 
-struct bt_dis *bt_dis_new(void *primary)
+static void foreach_dis_char(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct bt_dis *dis = user_data;
+       bt_uuid_t pnpid_uuid, uuid;
+       uint16_t value_handle;
+
+       /* Ignore if there are multiple instances */
+       if (dis->handle)
+               return;
+
+       if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid))
+               return;
+
+       /* Find PNPID characteristic's value handle */
+       bt_string_to_uuid(&pnpid_uuid, PNPID_UUID);
+       if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0)
+               dis->handle = value_handle;
+}
+
+static void foreach_dis_service(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct bt_dis *dis = user_data;
+
+       /* Ignore if there are multiple instances */
+       if (dis->handle)
+               return;
+
+       gatt_db_service_foreach_char(attr, foreach_dis_char, dis);
+}
+
+struct bt_dis *bt_dis_new(struct gatt_db *db)
+{
+       struct bt_dis *dis;
+
+       dis = g_try_new0(struct bt_dis, 1);
+       if (!dis)
+               return NULL;
+
+       dis->gatt_op = queue_new();
+
+       if (db) {
+               bt_uuid_t uuid;
+
+               /* Handle the DIS service */
+               bt_uuid16_create(&uuid, DIS_UUID16);
+               gatt_db_foreach_service(db, &uuid, foreach_dis_service, dis);
+               if (!dis->handle) {
+                       dis_free(dis);
+                       return NULL;
+               }
+       }
+
+       return bt_dis_ref(dis);
+}
+
+struct bt_dis *bt_dis_new_primary(void *primary)
 {
        struct bt_dis *dis;
 
@@ -251,7 +309,7 @@ bool bt_dis_attach(struct bt_dis *dis, void *attrib)
 {
        struct gatt_primary *primary = dis->primary;
 
-       if (dis->attrib || !primary)
+       if (dis->attrib)
                return false;
 
        dis->attrib = g_attrib_ref(attrib);
@@ -260,6 +318,8 @@ bool bt_dis_attach(struct bt_dis *dis, void *attrib)
                discover_char(dis, dis->attrib, primary->range.start,
                                                primary->range.end, NULL,
                                                configure_deviceinfo_cb, dis);
+       else
+               read_char(dis, attrib, dis->handle, read_pnpid_cb, dis);
 
        return true;
 }
index faf27b3..305ba1a 100755 (executable)
@@ -23,7 +23,8 @@
 
 struct bt_dis;
 
-struct bt_dis *bt_dis_new(void *primary);
+struct bt_dis *bt_dis_new(struct gatt_db *db);
+struct bt_dis *bt_dis_new_primary(void *primary);
 
 struct bt_dis *bt_dis_ref(struct bt_dis *dis);
 void bt_dis_unref(struct bt_dis *dis);
index ac9d4ac..b009be9 100755 (executable)
@@ -316,7 +316,7 @@ static int gap_accept(struct btd_service *service)
        }
 
        gas->db = gatt_db_ref(db);
-       gas->client = bt_gatt_client_ref(client);
+       gas->client = bt_gatt_client_clone(client);
 
        /* Handle the GAP services */
        bt_uuid16_create(&gap_uuid, GAP_UUID16);
index bac6cb2..bd9326d 100644 (file)
@@ -1200,23 +1200,27 @@ static gboolean input_device_auto_reconnect(gpointer user_data)
         */
        if (device_is_temporary(idev->device) ||
                                        btd_device_is_connected(idev->device))
-               return FALSE;
+               goto bail;
 
        /* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */
        if (idev->reconnect_attempt >= 6)
-               return FALSE;
+               goto bail;
 
        /* Check if the profile is already connected. */
        if (idev->ctrl_io)
-               return FALSE;
+               goto bail;
 
        if (is_connected(idev))
-               return FALSE;
+               goto bail;
 
        idev->reconnect_attempt++;
        dev_connect(idev);
 
        return TRUE;
+
+bail:
+       idev->reconnect_timer = 0;
+       return FALSE;
 }
 
 static const char * const _reconnect_mode_str[] = {
index e376c2b..dab385f 100755 (executable)
@@ -45,6 +45,8 @@
 #include "src/shared/util.h"
 #include "src/shared/uhid.h"
 #include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
 #include "src/log.h"
 
 #include "attrib/att.h"
@@ -59,6 +61,7 @@
 #include "profiles/input/hog-lib.h"
 
 #define HOG_UUID               "00001812-0000-1000-8000-00805f9b34fb"
+#define HOG_UUID16             0x1812
 
 #define HOG_INFO_UUID          0x2A4A
 #define HOG_REPORT_MAP_UUID    0x2A4B
@@ -83,6 +86,7 @@ struct bt_hog {
        uint16_t                vendor;
        uint16_t                product;
        uint16_t                version;
+       struct gatt_db_attribute *attr;
        struct gatt_primary     *primary;
        GAttrib                 *attrib;
        GSList                  *reports;
@@ -110,9 +114,11 @@ struct report {
        struct bt_hog           *hog;
        uint8_t                 id;
        uint8_t                 type;
+       uint16_t                handle;
+       uint16_t                value_handle;
+       uint8_t                 properties;
        uint16_t                ccc_handle;
        guint                   notifyid;
-       struct gatt_char        *decl;
        uint16_t                len;
        uint8_t                 *value;
 };
@@ -181,6 +187,10 @@ static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
        struct gatt_request *req;
        unsigned int id;
 
+       /* Ignore if not connected */
+       if (!attrib)
+               return;
+
        req = create_request(hog, user_data);
        if (!req)
                return;
@@ -334,7 +344,7 @@ static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
 
        report->notifyid = g_attrib_register(hog->attrib,
                                        ATT_OP_HANDLE_NOTIFY,
-                                       report->decl->value_handle,
+                                       report->value_handle,
                                        report_value_cb, report, NULL);
 
        DBG("Report characteristic descriptor written: notifications enabled");
@@ -403,7 +413,7 @@ static void report_reference_cb(guint8 status, const guint8 *pdu,
        report->id = pdu[1];
        report->type = pdu[2];
 
-       DBG("Report 0x%04x: id 0x%02x type %s", report->decl->value_handle,
+       DBG("Report 0x%04x: id 0x%02x type %s", report->value_handle,
                                report->id, type_to_string(report->type));
 
        /* Enable notifications only for Input Reports */
@@ -516,7 +526,7 @@ static int report_chrc_cmp(const void *data, const void *user_data)
        const struct report *report = data;
        const struct gatt_char *decl = user_data;
 
-       return report->decl->handle - decl->handle;
+       return report->handle - decl->handle;
 }
 
 static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
@@ -531,7 +541,9 @@ static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
 
        report = g_new0(struct report, 1);
        report->hog = hog;
-       report->decl = g_memdup(chr, sizeof(*chr));
+       report->handle = chr->handle;
+       report->value_handle = chr->value_handle;
+       report->properties = chr->properties;
        hog->reports = g_slist_append(hog->reports, report);
 
        read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
@@ -691,51 +703,19 @@ static void forward_report(struct uhid_event *ev, void *user_data)
        }
 
        DBG("Sending report type %d ID %d to handle 0x%X", report->type,
-                               report->id, report->decl->value_handle);
+                               report->id, report->value_handle);
 
        if (hog->attrib == NULL)
                return;
 
-       if (report->decl->properties & GATT_CHR_PROP_WRITE)
-               write_char(hog, hog->attrib, report->decl->value_handle,
+       if (report->properties & GATT_CHR_PROP_WRITE)
+               write_char(hog, hog->attrib, report->value_handle,
                                data, size, output_written_cb, hog);
-       else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
-               gatt_write_cmd(hog->attrib, report->decl->value_handle,
+       else if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+               gatt_write_cmd(hog->attrib, report->value_handle,
                                                data, size, NULL, NULL);
 }
 
-static void get_feature(struct uhid_event *ev, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-       struct report *report;
-       struct uhid_event rsp;
-       int err;
-
-       memset(&rsp, 0, sizeof(rsp));
-       rsp.type = UHID_FEATURE_ANSWER;
-       rsp.u.feature_answer.id = ev->u.feature.id;
-
-       report = find_report_by_rtype(hog, ev->u.feature.rtype,
-                                                       ev->u.feature.rnum);
-       if (!report) {
-               rsp.u.feature_answer.err = ENOTSUP;
-               goto done;
-       }
-
-       if (!report->value) {
-               rsp.u.feature_answer.err = EIO;
-               goto done;
-       }
-
-       rsp.u.feature_answer.size = report->len;
-       memcpy(rsp.u.feature_answer.data, report->value, report->len);
-
-done:
-       err = bt_uhid_send(hog->uhid, &rsp);
-       if (err < 0)
-               error("bt_uhid_send: %s", strerror(-err));
-}
-
 static void set_report_cb(guint8 status, const guint8 *pdu,
                                        guint16 plen, gpointer user_data)
 {
@@ -789,13 +769,13 @@ static void set_report(struct uhid_event *ev, void *user_data)
        }
 
        DBG("Sending report type %d ID %d to handle 0x%X", report->type,
-                               report->id, report->decl->value_handle);
+                               report->id, report->value_handle);
 
        if (hog->attrib == NULL)
                return;
 
        hog->setrep_att = gatt_write_char(hog->attrib,
-                                               report->decl->value_handle,
+                                               report->value_handle,
                                                data, size, set_report_cb,
                                                hog);
        if (!hog->setrep_att) {
@@ -878,7 +858,7 @@ static void get_report(struct uhid_event *ev, void *user_data)
        }
 
        hog->getrep_att = gatt_read_char(hog->attrib,
-                                               report->decl->value_handle,
+                                               report->value_handle,
                                                get_report_cb, hog);
        if (!hog->getrep_att) {
                err = ENOMEM;
@@ -1034,7 +1014,6 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
        }
 
        bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
-       bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
        bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
        bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
 
@@ -1180,7 +1159,6 @@ static void report_free(void *data)
        struct report *report = data;
 
        g_free(report->value);
-       g_free(report->decl);
        g_free(report);
 }
 
@@ -1211,14 +1189,132 @@ static void hog_free(void *data)
 
 struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
                                        uint16_t product, uint16_t version,
-                                       void *primary)
+                                       struct gatt_db *db)
 {
-       return bt_hog_new(-1, name, vendor, product, version, primary);
+       return bt_hog_new(-1, name, vendor, product, version, db);
 }
 
-struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+static void foreach_hog_report(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct report *report = user_data;
+       struct bt_hog *hog = report->hog;
+       const bt_uuid_t *uuid;
+       bt_uuid_t ref_uuid, ccc_uuid;
+       uint16_t handle;
+
+       handle = gatt_db_attribute_get_handle(attr);
+       uuid = gatt_db_attribute_get_type(attr);
+
+       bt_uuid16_create(&ref_uuid, GATT_REPORT_REFERENCE);
+       if (!bt_uuid_cmp(&ref_uuid, uuid)) {
+               read_char(hog, hog->attrib, handle, report_reference_cb,
+                                                               report);
+               return;
+       }
+
+       bt_uuid16_create(&ccc_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       if (!bt_uuid_cmp(&ccc_uuid, uuid))
+               report->ccc_handle = handle;
+}
+
+static int report_attr_cmp(const void *data, const void *user_data)
+{
+       const struct report *report = data;
+       const struct gatt_db_attribute *attr = user_data;
+
+       return report->handle - gatt_db_attribute_get_handle(attr);
+}
+
+static struct report *report_add(struct bt_hog *hog,
+                                       struct gatt_db_attribute *attr)
+{
+       struct report *report;
+       GSList *l;
+
+       /* Skip if report already exists */
+       l = g_slist_find_custom(hog->reports, attr, report_attr_cmp);
+       if (l)
+               return l->data;
+
+       report = g_new0(struct report, 1);
+       report->hog = hog;
+
+       gatt_db_attribute_get_char_data(attr, &report->handle,
+                                       &report->value_handle,
+                                       &report->properties,
+                                       NULL, NULL);
+
+       hog->reports = g_slist_append(hog->reports, report);
+
+       read_char(hog, hog->attrib, report->value_handle, report_read_cb,
+                                                               report);
+
+       return report;
+}
+
+static void foreach_hog_external(struct gatt_db_attribute *attr,
+                                                       void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       const bt_uuid_t *uuid;
+       bt_uuid_t ext_uuid;
+       uint16_t handle;
+
+       handle = gatt_db_attribute_get_handle(attr);
+       uuid = gatt_db_attribute_get_type(attr);
+
+       bt_uuid16_create(&ext_uuid, GATT_EXTERNAL_REPORT_REFERENCE);
+       if (!bt_uuid_cmp(&ext_uuid, uuid))
+               read_char(hog, hog->attrib, handle,
+                                       external_report_reference_cb, hog);
+}
+
+static void foreach_hog_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+       bt_uuid_t uuid, report_uuid, report_map_uuid, info_uuid;
+       bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
+       uint16_t handle, value_handle;
+
+       gatt_db_attribute_get_char_data(attr, &handle, &value_handle, NULL,
+                                       NULL, &uuid);
+
+       bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
+       if (!bt_uuid_cmp(&report_uuid, &uuid)) {
+               struct report *report = report_add(hog, attr);
+               gatt_db_service_foreach_desc(attr, foreach_hog_report, report);
+               return;
+       }
+
+       bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
+       if (!bt_uuid_cmp(&report_map_uuid, &uuid)) {
+               read_char(hog, hog->attrib, value_handle, report_map_read_cb,
+                                                                       hog);
+               gatt_db_service_foreach_desc(attr, foreach_hog_external, hog);
+               return;
+       }
+
+       bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
+       if (!bt_uuid_cmp(&info_uuid, &uuid)) {
+               read_char(hog, hog->attrib, value_handle, info_read_cb, hog);
+               return;
+       }
+
+       bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
+       if (!bt_uuid_cmp(&proto_mode_uuid, &uuid)) {
+               hog->proto_mode_handle = value_handle;
+               read_char(hog, hog->attrib, value_handle, proto_mode_read_cb,
+                                                                       hog);
+       }
+
+       bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
+       if (!bt_uuid_cmp(&ctrlpt_uuid, &uuid))
+               hog->ctrlpt_handle = value_handle;
+}
+
+static struct bt_hog *hog_new(int fd, const char *name, uint16_t vendor,
                                        uint16_t product, uint16_t version,
-                                       void *primary)
+                                       struct gatt_db_attribute *attr)
 {
        struct bt_hog *hog;
 
@@ -1245,9 +1341,74 @@ struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
        hog->vendor = vendor;
        hog->product = product;
        hog->version = version;
+       hog->attr = attr;
+
+       return hog;
+}
+
+static void hog_attach_instace(struct bt_hog *hog,
+                               struct gatt_db_attribute *attr)
+{
+       struct bt_hog *instance;
+
+       if (!hog->attr) {
+               hog->attr = attr;
+               gatt_db_service_foreach_char(hog->attr, foreach_hog_chrc, hog);
+               return;
+       }
+
+       instance = hog_new(hog->uhid_fd, hog->name, hog->vendor,
+                                       hog->product, hog->version, attr);
+       if (!instance)
+               return;
+
+       hog->instances = g_slist_append(hog->instances, instance);
+}
 
-       if (primary)
-               hog->primary = g_memdup(primary, sizeof(*hog->primary));
+static void foreach_hog_service(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+
+       hog_attach_instace(hog, attr);
+}
+
+static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
+                                       uint16_t version, void *user_data)
+{
+       struct bt_hog *hog = user_data;
+
+       hog->vendor = vendor;
+       hog->product = product;
+       hog->version = version;
+}
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       struct gatt_db *db)
+{
+       struct bt_hog *hog;
+
+       hog = hog_new(fd, name, vendor, product, version, NULL);
+       if (!hog)
+               return NULL;
+
+       if (db) {
+               bt_uuid_t uuid;
+
+               /* Handle the HID services */
+               bt_uuid16_create(&uuid, HOG_UUID16);
+               gatt_db_foreach_service(db, &uuid, foreach_hog_service, hog);
+               if (!hog->attr) {
+                       hog_free(hog);
+                       return NULL;
+               }
+
+               /* Try creating a DIS instance in case pid/vid are not set */
+               if (!vendor && !product) {
+                       hog->dis = bt_dis_new(db);
+                       bt_dis_set_notification(hog->dis, dis_notify, hog);
+               }
+       }
 
        return bt_hog_ref(hog);
 }
@@ -1308,16 +1469,6 @@ static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
                bt_scpp_attach(hog->scpp, hog->attrib);
 }
 
-static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
-                                       uint16_t version, void *user_data)
-{
-       struct bt_hog *hog = user_data;
-
-       hog->vendor = vendor;
-       hog->product = product;
-       hog->version = version;
-}
-
 static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
 {
        if (hog->dis) {
@@ -1325,7 +1476,7 @@ static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
                return;
        }
 
-       hog->dis = bt_dis_new(primary);
+       hog->dis = bt_dis_new_primary(primary);
        if (hog->dis) {
                bt_dis_set_notification(hog->dis, dis_notify, hog);
                bt_dis_attach(hog->dis, hog->attrib);
@@ -1357,10 +1508,11 @@ static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
        }
 
        instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
-                                       hog->product, hog->version, primary);
+                                       hog->product, hog->version, NULL);
        if (!instance)
                return;
 
+       instance->primary = g_memdup(primary, sizeof(*primary));
        find_included(instance, hog->attrib, primary->range.start,
                        primary->range.end, find_included_cb, instance);
 
@@ -1415,7 +1567,6 @@ static void primary_cb(uint8_t status, GSList *services, void *user_data)
 
 bool bt_hog_attach(struct bt_hog *hog, void *gatt)
 {
-       struct gatt_primary *primary = hog->primary;
        GSList *l;
 
        if (hog->attrib)
@@ -1423,7 +1574,7 @@ bool bt_hog_attach(struct bt_hog *hog, void *gatt)
 
        hog->attrib = g_attrib_ref(gatt);
 
-       if (!primary) {
+       if (!hog->attr && !hog->primary) {
                discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
                return true;
        }
@@ -1444,9 +1595,14 @@ bool bt_hog_attach(struct bt_hog *hog, void *gatt)
 
        if (!hog->uhid_created) {
                DBG("HoG discovering characteristics");
-               discover_char(hog, hog->attrib, primary->range.start,
-                                               primary->range.end, NULL,
-                                               char_discovered_cb, hog);
+               if (hog->attr)
+                       gatt_db_service_foreach_char(hog->attr,
+                                                       foreach_hog_chrc, hog);
+               else
+                       discover_char(hog, hog->attrib,
+                                       hog->primary->range.start,
+                                       hog->primary->range.end, NULL,
+                                       char_discovered_cb, hog);
                return true;
        }
 
@@ -1455,7 +1611,7 @@ bool bt_hog_attach(struct bt_hog *hog, void *gatt)
 
                r->notifyid = g_attrib_register(hog->attrib,
                                        ATT_OP_HANDLE_NOTIFY,
-                                       r->decl->value_handle,
+                                       r->value_handle,
                                        report_value_cb, r, NULL);
        }
 
@@ -1528,14 +1684,14 @@ int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
        if (!report)
                return -ENOTSUP;
 
-       DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
+       DBG("hog: Write report, handle 0x%X", report->value_handle);
 
-       if (report->decl->properties & GATT_CHR_PROP_WRITE)
-               write_char(hog, hog->attrib, report->decl->value_handle,
+       if (report->properties & GATT_CHR_PROP_WRITE)
+               write_char(hog, hog->attrib, report->value_handle,
                                data, size, output_written_cb, hog);
 
-       if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
-               gatt_write_cmd(hog->attrib, report->decl->value_handle,
+       if (report->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+               gatt_write_cmd(hog->attrib, report->value_handle,
                                                data, size, NULL, NULL);
 
        for (l = hog->instances; l; l = l->next) {
index 2a9b899..415dc63 100755 (executable)
@@ -25,11 +25,11 @@ struct bt_hog;
 
 struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
                                        uint16_t product, uint16_t version,
-                                       void *primary);
+                                       struct gatt_db *db);
 
 struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
                                        uint16_t product, uint16_t version,
-                                       void *primary);
+                                       struct gatt_db *db);
 
 struct bt_hog *bt_hog_ref(struct bt_hog *hog);
 void bt_hog_unref(struct bt_hog *hog);
index 9836a6c..23c9c15 100755 (executable)
@@ -60,7 +60,6 @@
 #define HOG_UUID               "00001812-0000-1000-8000-00805f9b34fb"
 
 struct hog_device {
-       guint                   attioid;
        struct btd_device       *device;
        struct bt_hog           *hog;
 };
@@ -68,32 +67,34 @@ struct hog_device {
 static gboolean suspend_supported = FALSE;
 static struct queue *devices = NULL;
 
-static struct hog_device *hog_device_new(struct btd_device *device,
-                                               struct gatt_primary *prim)
+static void hog_device_accept(struct hog_device *dev, struct gatt_db *db)
 {
-       struct hog_device *dev;
        char name[248];
        uint16_t vendor, product, version;
 
-       if (device_name_known(device))
-               device_get_name(device, name, sizeof(name));
+       if (dev->hog)
+               return;
+
+       if (device_name_known(dev->device))
+               device_get_name(dev->device, name, sizeof(name));
        else
                strcpy(name, "bluez-hog-device");
 
-       vendor = btd_device_get_vendor(device);
-       product = btd_device_get_product(device);
-       version = btd_device_get_version(device);
+       vendor = btd_device_get_vendor(dev->device);
+       product = btd_device_get_product(dev->device);
+       version = btd_device_get_version(dev->device);
 
        DBG("name=%s vendor=0x%X, product=0x%X, version=0x%X", name, vendor,
                                                        product, version);
 
-       dev = new0(struct hog_device, 1);
-       dev->hog = bt_hog_new_default(name, vendor, product, version, prim);
-       if (!dev->hog) {
-               free(dev);
-               return NULL;
-       }
+       dev->hog = bt_hog_new_default(name, vendor, product, version, db);
+}
 
+static struct hog_device *hog_device_new(struct btd_device *device)
+{
+       struct hog_device *dev;
+
+       dev = new0(struct hog_device, 1);
        dev->device = btd_device_ref(device);
 
        if (!devices)
@@ -149,30 +150,16 @@ static int hog_probe(struct btd_service *service)
 {
        struct btd_device *device = btd_service_get_device(service);
        const char *path = device_get_path(device);
-       GSList *primaries, *l;
+       struct hog_device *dev;
 
        DBG("path %s", path);
 
-       primaries = btd_device_get_primaries(device);
-       if (primaries == NULL)
+       dev = hog_device_new(device);
+       if (!dev)
                return -EINVAL;
 
-       for (l = primaries; l; l = g_slist_next(l)) {
-               struct gatt_primary *prim = l->data;
-               struct hog_device *dev;
-
-               if (strcmp(prim->uuid, HOG_UUID) != 0)
-                       continue;
-
-               dev = hog_device_new(device, prim);
-               if (!dev)
-                       break;
-
-               btd_service_set_user_data(service, dev);
-               return 0;
-       }
-
-       return -EINVAL;
+       btd_service_set_user_data(service, dev);
+       return 0;
 }
 
 static void hog_remove(struct btd_service *service)
@@ -190,8 +177,15 @@ static int hog_accept(struct btd_service *service)
 {
        struct hog_device *dev = btd_service_get_user_data(service);
        struct btd_device *device = btd_service_get_device(service);
+       struct gatt_db *db = btd_device_get_gatt_db(device);
        GAttrib *attrib = btd_device_get_attrib(device);
 
+       if (!dev->hog) {
+               hog_device_accept(dev, db);
+               if (!dev->hog)
+                       return -EINVAL;
+       }
+
        /* TODO: Replace GAttrib with bt_gatt_client */
        bt_hog_attach(dev->hog, attrib);
 
index 2ee3b9b..9a59985 100755 (executable)
@@ -43,6 +43,7 @@
 #include "src/device.h"
 #include "src/profile.h"
 
+#include "sixaxis.h"
 #include "device.h"
 #include "server.h"
 
@@ -126,6 +127,7 @@ static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
 {
        struct btd_device *device;
        uint16_t vid, pid;
+       const struct cable_pairing *cp;
 
        device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
        if (!device)
@@ -134,16 +136,9 @@ static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
        vid = btd_device_get_vendor(device);
        pid = btd_device_get_product(device);
 
-       /* DualShock 3 */
-       if (vid == 0x054c && pid == 0x0268)
-               return true;
-
-       /* DualShock 4 */
-       if (vid == 0x054c && pid == 0x05c4)
-               return true;
-
-       /* Navigation Controller */
-       if (vid == 0x054c && pid == 0x042f)
+       cp = get_pairing(vid, pid);
+       if (cp && (cp->type == CABLE_PAIRING_SIXAXIS ||
+                                       cp->type == CABLE_PAIRING_DS4))
                return true;
 
        return false;
diff --git a/profiles/input/sixaxis.h b/profiles/input/sixaxis.h
new file mode 100644 (file)
index 0000000..8e6f3cc
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009,2017  Bastien Nocera <hadess@hadess.net>
+ *  Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+ *  Copyright (C) 2013  Szymon Janc <szymon.janc@gmail.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _SIXAXIS_H_
+#define _SIXAXIS_H_
+
+typedef enum {
+       CABLE_PAIRING_UNSUPPORTED = 0,
+       CABLE_PAIRING_SIXAXIS,
+       CABLE_PAIRING_DS4,
+} CablePairingType;
+
+struct cable_pairing {
+       const char *name;
+       uint16_t source;
+       uint16_t vid;
+       uint16_t pid;
+       uint16_t version;
+       CablePairingType type;
+};
+
+static inline const struct cable_pairing *
+get_pairing(uint16_t vid, uint16_t pid)
+{
+       static const struct cable_pairing devices[] = {
+               {
+                       .name = "Sony PLAYSTATION(R)3 Controller",
+                       .source = 0x0002,
+                       .vid = 0x054c,
+                       .pid = 0x0268,
+                       .version = 0x0000,
+                       .type = CABLE_PAIRING_SIXAXIS,
+               },
+               {
+                       .name = "Navigation Controller",
+                       .source = 0x0002,
+                       .vid = 0x054c,
+                       .pid = 0x042f,
+                       .version = 0x0000,
+                       .type = CABLE_PAIRING_SIXAXIS,
+               },
+               {
+                       .name = "Wireless Controller",
+                       .source = 0x0002,
+                       .vid = 0x054c,
+                       .pid = 0x05c4,
+                       .version = 0x0001,
+                       .type = CABLE_PAIRING_DS4,
+               },
+               {
+                       .name = "Wireless Controller",
+                       .source = 0x0002,
+                       .vid = 0x054c,
+                       .pid = 0x09cc,
+                       .version = 0x0001,
+                       .type = CABLE_PAIRING_DS4,
+               },
+       };
+       guint i;
+
+       for (i = 0; i < G_N_ELEMENTS(devices); i++) {
+               if (devices[i].vid != vid)
+                       continue;
+               if (devices[i].pid != pid)
+                       continue;
+
+               return &devices[i];
+       }
+
+       return NULL;
+}
+
+#endif /* _SIXAXIS_H_ */
diff --git a/profiles/midi/libmidi.c b/profiles/midi/libmidi.c
new file mode 100644 (file)
index 0000000..4b4df79
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ *  Copyright (C) 2016 ROLI Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#include <glib.h>
+
+/* Avoid linkage problem on unit-tests */
+#ifndef MIDI_TEST
+#include "src/backtrace.h"
+#define MIDI_ASSERT(_expr) btd_assert(_expr)
+#else
+#define MIDI_ASSERT(_expr) g_assert(_expr)
+#endif
+#include "libmidi.h"
+
+inline static void buffer_append_byte(struct midi_buffer *buffer,
+                                      const uint8_t byte)
+{
+       buffer->data[buffer->len++] = byte;
+}
+
+inline static void buffer_append_data(struct midi_buffer *buffer,
+                                      const uint8_t *data, size_t size)
+{
+       memcpy(buffer->data + buffer->len, data, size);
+       buffer->len += size;
+}
+
+inline static uint8_t buffer_reverse_get(struct midi_buffer *buffer, size_t i)
+{
+       MIDI_ASSERT(buffer->len > i);
+       return buffer->data[buffer->len - (i + 1)];
+}
+
+inline static void buffer_reverse_set(struct midi_buffer *buffer, size_t i,
+                                      const uint8_t byte)
+{
+       MIDI_ASSERT(buffer->len > i);
+       buffer->data[buffer->len - (i + 1)] = byte;
+}
+
+inline static size_t parser_get_available_size(struct midi_write_parser *parser)
+{
+       return parser->stream_size - parser->midi_stream.len;
+}
+
+inline static uint8_t sysex_get(const snd_seq_event_t *ev, size_t i)
+{
+       MIDI_ASSERT(ev->data.ext.len > i);
+       return ((uint8_t*)ev->data.ext.ptr)[i];
+}
+
+inline static void append_timestamp_high_maybe(struct midi_write_parser *parser)
+{
+       uint8_t timestamp_high = 0x80;
+
+       if (midi_write_has_data(parser))
+               return;
+
+       parser->rtime = g_get_monotonic_time() / 1000; /* convert µs to ms */
+       timestamp_high |= (parser->rtime & 0x1F80) >> 7;
+       /* set timestampHigh */
+       buffer_append_byte(&parser->midi_stream, timestamp_high);
+}
+
+inline static void append_timestamp_low(struct midi_write_parser *parser)
+{
+       const uint8_t timestamp_low = 0x80 | (parser->rtime & 0x7F);
+       buffer_append_byte(&parser->midi_stream, timestamp_low);
+}
+
+int midi_write_init(struct midi_write_parser *parser, size_t buffer_size)
+{
+       int err;
+
+       parser->rtime = 0;
+       parser->rstatus = SND_SEQ_EVENT_NONE;
+       parser->stream_size = buffer_size;
+
+       parser->midi_stream.data = malloc(buffer_size);
+       if (!parser->midi_stream.data)
+               return -ENOMEM;
+
+       parser->midi_stream.len = 0;
+
+       err = snd_midi_event_new(buffer_size, &parser->midi_ev);
+       if (err < 0)
+               free(parser->midi_stream.data);
+
+       return err;
+}
+
+int midi_read_init(struct midi_read_parser *parser)
+{
+       int err;
+
+       parser->rstatus = 0;
+       parser->rtime = -1;
+       parser->timestamp = 0;
+       parser->timestamp_low = 0;
+       parser->timestamp_high = 0;
+
+       parser->sysex_stream.data = malloc(MIDI_SYSEX_MAX_SIZE);
+       if (!parser->sysex_stream.data)
+               return -ENOMEM;
+
+       parser->sysex_stream.len = 0;
+
+       err = snd_midi_event_new(MIDI_MSG_MAX_SIZE, &parser->midi_ev);
+       if (err < 0)
+               free(parser->sysex_stream.data);
+
+       return err;
+}
+
+/* Algorithm:
+   1) check initial timestampLow:
+       if used_sysex == 0, then tsLow = 1, else tsLow = 0
+   2) calculate sysex size of current packet:
+   2a) first check special case:
+       if midi->out_length - 1 (tsHigh) - tsLow ==
+          sysex_length - used_sysex
+       size is: min(midi->out_length - 1 - tsLow,
+                    sysex_length - used_sysex - 1)
+   2b) else size is: min(midi->out_length - 1 - tsLow,
+                     sysex_length - used_sysex)
+   3) check if packet contains F7: fill respective tsLow byte
+*/
+static void read_ev_sysex(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+                          midi_read_ev_cb write_cb, void *user_data)
+{
+       unsigned int used_sysex = 0;
+
+       /* We need at least 2 bytes (timestampLow + F0) */
+       if (parser_get_available_size(parser) < 2) {
+               /* send current message and start new one */
+               write_cb(parser, user_data);
+               midi_write_reset(parser);
+               append_timestamp_high_maybe(parser);
+       }
+
+       /* timestampLow on initial F0 */
+       if (sysex_get(ev, 0) == 0xF0)
+               append_timestamp_low(parser);
+
+       do {
+               unsigned int size_of_sysex;
+
+               append_timestamp_high_maybe(parser);
+
+               size_of_sysex = MIN(parser_get_available_size(parser),
+                                   ev->data.ext.len - used_sysex);
+
+               if (parser_get_available_size(parser) == ev->data.ext.len - used_sysex)
+                       size_of_sysex--;
+
+               buffer_append_data(&parser->midi_stream,
+                                   ev->data.ext.ptr + used_sysex,
+                                   size_of_sysex);
+               used_sysex += size_of_sysex;
+
+               if (parser_get_available_size(parser) <= 1 &&
+                   buffer_reverse_get(&parser->midi_stream, 0) != 0xF7) {
+                       write_cb(parser, user_data);
+                       midi_write_reset(parser);
+               }
+       } while (used_sysex < ev->data.ext.len);
+
+       /* check for F7 and update respective timestampLow byte */
+       if (midi_write_has_data(parser) &&
+           buffer_reverse_get(&parser->midi_stream, 0) == 0xF7) {
+               /* remove 0xF7 from buffer, append timestamp and add 0xF7 back again */
+               parser->midi_stream.len--;
+               append_timestamp_low(parser);
+               buffer_append_byte(&parser->midi_stream, 0xF7);
+       }
+}
+
+static void read_ev_others(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+                           midi_read_ev_cb write_cb, void *user_data)
+{
+       int length;
+
+       /* check for running status */
+       if (parser->rstatus != ev->type) {
+               snd_midi_event_reset_decode(parser->midi_ev);
+               append_timestamp_low(parser);
+       }
+
+       /* each midi message has timestampLow byte to follow */
+       length = snd_midi_event_decode(parser->midi_ev,
+                                      parser->midi_stream.data +
+                                      parser->midi_stream.len,
+                                      parser_get_available_size(parser),
+                                      ev);
+
+       if (length == -ENOMEM) {
+               /* remove previously added timestampLow */
+               if (parser->rstatus != ev->type)
+                       parser->midi_stream.len--;
+               write_cb(parser, user_data);
+               /* cleanup state for next packet */
+               snd_midi_event_reset_decode(parser->midi_ev);
+               midi_write_reset(parser);
+               append_timestamp_high_maybe(parser);
+               append_timestamp_low(parser);
+               length = snd_midi_event_decode(parser->midi_ev,
+                                              parser->midi_stream.data +
+                                              parser->midi_stream.len,
+                                              parser_get_available_size(parser),
+                                              ev);
+       }
+
+       if (length > 0)
+               parser->midi_stream.len += length;
+}
+
+void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+                  midi_read_ev_cb write_cb, void *user_data)
+{
+       MIDI_ASSERT(write_cb);
+
+       append_timestamp_high_maybe(parser);
+
+       /* SysEx is special case:
+          SysEx has two timestampLow bytes, before F0 and F7
+       */
+       if (ev->type == SND_SEQ_EVENT_SYSEX)
+               read_ev_sysex(parser, ev, write_cb, user_data);
+       else
+               read_ev_others(parser, ev, write_cb, user_data);
+
+       parser->rstatus = ev->type;
+
+       if (parser_get_available_size(parser) == 0) {
+               write_cb(parser, user_data);
+               midi_write_reset(parser);
+       }
+}
+
+static void update_ev_timestamp(struct midi_read_parser *parser,
+                                snd_seq_event_t *ev, uint16_t ts_low)
+{
+       int delta_timestamp;
+       int delta_rtime;
+       int64_t rtime_current;
+       uint16_t timestamp;
+
+       /* time_low overwflow results on time_high to increment by one */
+       if (parser->timestamp_low > ts_low)
+               parser->timestamp_high++;
+
+       timestamp = (parser->timestamp_high << 7) | parser->timestamp_low;
+
+       rtime_current = g_get_monotonic_time() / 1000; /* convert µs to ms */
+       delta_timestamp = timestamp - (int)parser->timestamp;
+       delta_rtime = rtime_current - parser->rtime;
+
+       if (delta_rtime > MIDI_MAX_TIMESTAMP)
+               parser->rtime = rtime_current;
+       else {
+
+               /* If delta_timestamp is way to big than delta_rtime,
+                  this means that the device sent a message in the past,
+                  so we have to compensate for this. */
+               if (delta_timestamp > 7000 && delta_rtime < 1000)
+                       delta_timestamp = 0;
+
+               /* check if timestamp did overflow */
+               if (delta_timestamp < 0) {
+                       /* same timestamp in the past problem */
+                       if ((delta_timestamp + MIDI_MAX_TIMESTAMP) > 7000 &&
+                           delta_rtime < 1000)
+                               delta_timestamp = 0;
+                       else
+                               delta_timestamp = delta_timestamp + MIDI_MAX_TIMESTAMP;
+               }
+
+               parser->rtime += delta_timestamp;
+       }
+
+       parser->timestamp += delta_timestamp;
+       if (parser->timestamp > MIDI_MAX_TIMESTAMP)
+               parser->timestamp %= MIDI_MAX_TIMESTAMP + 1;
+
+       /* set event timestamp */
+       /* TODO: update event timestamp here! */
+}
+
+static size_t handle_end_of_sysex(struct midi_read_parser *parser,
+                                  snd_seq_event_t *ev,
+                                  const uint8_t *data,
+                                  size_t sysex_length)
+{
+       uint8_t time_low;
+
+       /* At this time, timestampLow is copied as the last byte,
+          instead of 0xF7 */
+       buffer_append_data(&parser->sysex_stream, data, sysex_length);
+
+       time_low = buffer_reverse_get(&parser->sysex_stream, 0) & 0x7F;
+
+       /* Remove timestamp byte */
+       buffer_reverse_set(&parser->sysex_stream, 0, 0xF7);
+
+       /* Update event */
+       update_ev_timestamp(parser, ev, time_low);
+       snd_seq_ev_set_sysex(ev, parser->sysex_stream.len,
+                            parser->sysex_stream.data);
+
+       return sysex_length + 1; /* +1 because of timestampLow */
+}
+
+
+
+size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data,
+                    size_t size, snd_seq_event_t *ev /* OUT */)
+{
+       size_t midi_size = 0;
+       size_t i = 0;
+       bool err = false;
+
+       if (parser->timestamp_high == 0)
+               parser->timestamp_high = data[i++] & 0x3F;
+
+       snd_midi_event_reset_encode(parser->midi_ev);
+
+       /* timestamp byte */
+       if (data[i] & 0x80) {
+               update_ev_timestamp(parser, ev, data[i] & 0x7F);
+
+               /* check for wrong BLE-MIDI message size */
+               if (++i == size) {
+                       err = true;
+                       goto _finish;
+               }
+       }
+
+       /* cleanup sysex_stream if message is broken or is a new SysEx */
+       if (data[i] >= 0x80 && data[i] != 0xF7 && parser->sysex_stream.len > 0)
+               parser->sysex_stream.len = 0;
+
+       switch (data[i]) {
+       case 0xF8 ... 0XFF:
+               /* System Real-Time Messages */
+               midi_size = 1;
+               break;
+
+               /* System Common Messages */
+       case 0xF0: /* SysEx Start */ {
+               uint8_t *pos;
+
+               /* cleanup Running Status Message */
+               parser->rstatus = 0;
+
+               /* Avoid parsing if SysEx is contained in one BLE packet */
+               if ((pos = memchr(data + i, 0xF7, size - i))) {
+                       const size_t sysex_length = pos - (data + i);
+                       midi_size = handle_end_of_sysex(parser, ev, data + i,
+                                                       sysex_length);
+               } else {
+                       buffer_append_data(&parser->sysex_stream, data + i, size - i);
+                       err = true; /* Not an actual error, just incomplete message */
+                       midi_size = size - i;
+               }
+
+               goto _finish;
+       }
+
+       case 0xF1:
+       case 0xF3:
+               midi_size = 2;
+               break;
+       case 0xF2:
+               midi_size = 3;
+               break;
+       case 0xF4:
+       case 0xF5: /* Ignore */
+               i++;
+               err = true;
+               goto _finish;
+               break;
+       case 0xF6:
+               midi_size = 1;
+               break;
+       case 0xF7: /* SysEx End */
+               buffer_append_byte(&parser->sysex_stream, 0xF7);
+               snd_seq_ev_set_sysex(ev, parser->sysex_stream.len,
+                                    parser->sysex_stream.data);
+
+               midi_size = 1; /* timestampLow was alredy processed */
+               goto _finish;
+
+       case 0x80 ... 0xEF:
+               /*
+                * Channel Voice Messages, Channel Mode Messages
+                * and Control Change Messages.
+                */
+               parser->rstatus = data[i];
+               midi_size = (data[i] >= 0xC0 && data[i] <= 0xDF) ? 2 : 3;
+               break;
+
+       case 0x00 ... 0x7F:
+
+               /* Check for SysEx messages */
+               if (parser->sysex_stream.len > 0) {
+                       uint8_t *pos;
+
+                       if ((pos = memchr(data + i, 0xF7, size - i))) {
+                               const size_t sysex_length = pos - (data + i);
+                               midi_size = handle_end_of_sysex(parser, ev, data + i,
+                                                               sysex_length);
+                       } else {
+                               buffer_append_data(&parser->sysex_stream, data + i, size - i);
+                               err = true; /* Not an actual error, just incomplete message */
+                               midi_size = size - i;
+                       }
+
+                       goto _finish;
+               }
+
+               /* Running State Message was not set */
+               if (parser->rstatus == 0) {
+                       midi_size = 1;
+                       err = true;
+                       goto _finish;
+               }
+
+               snd_midi_event_encode_byte(parser->midi_ev, parser->rstatus, ev);
+               midi_size = (parser->rstatus >= 0xC0 && parser->rstatus <= 0xDF) ? 1 : 2;
+               break;
+       }
+
+       if ((i + midi_size) > size) {
+               err = true;
+               goto _finish;
+       }
+
+       snd_midi_event_encode(parser->midi_ev, data + i, midi_size, ev);
+
+_finish:
+       if (err)
+               ev->type = SND_SEQ_EVENT_NONE;
+
+       return i + midi_size;
+}
diff --git a/profiles/midi/libmidi.h b/profiles/midi/libmidi.h
new file mode 100644 (file)
index 0000000..9d94065
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ *  Copyright (C) 2016 ROLI Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifndef LIBMIDI_H
+#define LIBMIDI_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <alsa/asoundlib.h>
+
+#define MIDI_UUID "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
+#define MIDI_IO_UUID "7772E5DB-3868-4112-A1A9-F2669D106BF3"
+
+#define MIDI_MAX_TIMESTAMP 8191
+#define MIDI_MSG_MAX_SIZE 12
+#define MIDI_SYSEX_MAX_SIZE (4 * 1024)
+
+struct midi_buffer {
+       uint8_t *data;
+       size_t len;
+};
+
+/* MIDI I/O Write parser */
+
+struct midi_write_parser {
+       int64_t rtime;                  /* last writer's real time */
+       snd_seq_event_type_t rstatus;   /* running status event type */
+       struct midi_buffer midi_stream; /* MIDI I/O byte stream */
+       size_t stream_size;             /* what is the maximum size of the midi_stream array */
+       snd_midi_event_t *midi_ev;      /* midi<->seq event */
+};
+
+int midi_write_init(struct midi_write_parser *parser, size_t buffer_size);
+
+static inline void midi_write_free(struct midi_write_parser *parser)
+{
+       free(parser->midi_stream.data);
+       snd_midi_event_free(parser->midi_ev);
+}
+
+static inline void midi_write_reset(struct midi_write_parser *parser)
+{
+       parser->rstatus = SND_SEQ_EVENT_NONE;
+       parser->midi_stream.len = 0;
+}
+
+static inline bool midi_write_has_data(const struct midi_write_parser *parser)
+{
+       return parser->midi_stream.len > 0;
+}
+
+static inline const uint8_t * midi_write_data(const struct midi_write_parser *parser)
+{
+       return parser->midi_stream.data;
+}
+
+static inline size_t midi_write_data_size(const struct midi_write_parser *parser)
+{
+       return parser->midi_stream.len;
+}
+
+typedef void (*midi_read_ev_cb)(const struct midi_write_parser *parser, void *);
+
+/* It creates BLE-MIDI raw packets from the a sequencer event. If the packet
+   is full, then it calls write_cb and resets its internal state as many times
+   as necessary.
+ */
+void midi_read_ev(struct midi_write_parser *parser, const snd_seq_event_t *ev,
+                  midi_read_ev_cb write_cb, void *user_data);
+
+/* MIDI I/O Read parser */
+
+struct midi_read_parser {
+       uint8_t rstatus;                 /* running status byte */
+       int64_t rtime;                   /* last reader's real time */
+       int16_t timestamp;               /* last MIDI-BLE timestamp */
+       int8_t timestamp_low;            /* MIDI-BLE timestampLow from the current packet */
+       int8_t timestamp_high;           /* MIDI-BLE timestampHigh from the current packet */
+       struct midi_buffer sysex_stream; /* SysEx stream */
+       snd_midi_event_t *midi_ev;       /* midi<->seq event */
+};
+
+int midi_read_init(struct midi_read_parser *parser);
+
+static inline void midi_read_free(struct midi_read_parser *parser)
+{
+       free(parser->sysex_stream.data);
+       snd_midi_event_free(parser->midi_ev);
+}
+
+static inline void midi_read_reset(struct midi_read_parser *parser)
+{
+       parser->rstatus = 0;
+       parser->timestamp_low = 0;
+       parser->timestamp_high = 0;
+}
+
+/* Parses raw BLE-MIDI messages and populates a sequencer event representing the
+   current MIDI message. It returns how much raw data was processed.
+ */
+size_t midi_read_raw(struct midi_read_parser *parser, const uint8_t *data,
+                     size_t size, snd_seq_event_t *ev /* OUT */);
+
+#endif /* LIBMIDI_H */
diff --git a/profiles/midi/midi.c b/profiles/midi/midi.c
new file mode 100644 (file)
index 0000000..fdc1c00
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ *  Copyright (C) 2016 ROLI Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Information about this plugin:
+ *
+ *  This plugin implements the MIDI over Bluetooth Low-Energy (BLE-MIDI) 1.0
+ *  specification as published by MMA in November/2015.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <alsa/asoundlib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-client.h"
+#include "src/shared/io.h"
+#include "src/log.h"
+#include "attrib/att.h"
+
+#include "libmidi.h"
+
+struct midi {
+       struct btd_device *dev;
+       struct gatt_db *db;
+       struct bt_gatt_client *client;
+       unsigned int io_cb_id;
+       struct io *io;
+       uint16_t midi_io_handle;
+
+       /* ALSA handlers */
+       snd_seq_t *seq_handle;
+       int seq_client_id;
+       int seq_port_id;
+
+       /* MIDI parser*/
+       struct midi_read_parser midi_in;
+       struct midi_write_parser midi_out;
+};
+
+static bool midi_write_cb(struct io *io, void *user_data)
+{
+       struct midi *midi = user_data;
+       int err;
+
+       void foreach_cb(const struct midi_write_parser *parser, void *user_data) {
+               struct midi *midi = user_data;
+               bt_gatt_client_write_without_response(midi->client,
+                                                     midi->midi_io_handle,
+                                                     false,
+                                                     midi_write_data(parser),
+                                                     midi_write_data_size(parser));
+       };
+
+       do {
+               snd_seq_event_t *event = NULL;
+
+               err = snd_seq_event_input(midi->seq_handle, &event);
+
+               if (err < 0 || !event)
+                       break;
+
+               midi_read_ev(&midi->midi_out, event, foreach_cb, midi);
+
+       } while (err > 0);
+
+       if (midi_write_has_data(&midi->midi_out))
+               bt_gatt_client_write_without_response(midi->client,
+                                                     midi->midi_io_handle,
+                                                     false,
+                                                     midi_write_data(&midi->midi_out),
+                                                     midi_write_data_size(&midi->midi_out));
+
+       midi_write_reset(&midi->midi_out);
+
+       return true;
+}
+
+static void midi_io_value_cb(uint16_t value_handle, const uint8_t *value,
+                             uint16_t length, void *user_data)
+{
+       struct midi *midi = user_data;
+       snd_seq_event_t ev;
+       unsigned int i = 0;
+
+       if (length < 3) {
+               warn("MIDI I/O: Wrong packet format: length is %u bytes but it should "
+                    "be at least 3 bytes", length);
+               return;
+       }
+
+       snd_seq_ev_clear(&ev);
+       snd_seq_ev_set_source(&ev, midi->seq_port_id);
+       snd_seq_ev_set_subs(&ev);
+       snd_seq_ev_set_direct(&ev);
+
+       midi_read_reset(&midi->midi_in);
+
+       while (i < length) {
+               size_t count = midi_read_raw(&midi->midi_in, value + i, length - i, &ev);
+
+               if (count == 0)
+                       goto _err;
+
+               if (ev.type != SND_SEQ_EVENT_NONE)
+                       snd_seq_event_output_direct(midi->seq_handle, &ev);
+
+               i += count;
+       }
+
+       return;
+
+_err:
+       error("Wrong BLE-MIDI message");
+}
+
+static void midi_io_ccc_written_cb(uint16_t att_ecode, void *user_data)
+{
+       if (att_ecode != 0) {
+               error("MIDI I/O: notifications not enabled %s",
+                     att_ecode2str(att_ecode));
+               return;
+       }
+
+       DBG("MIDI I/O: notification enabled");
+}
+
+static void midi_io_initial_read_cb(bool success, uint8_t att_ecode,
+                                    const uint8_t *value, uint16_t length,
+                                    void *user_data)
+{
+       struct midi *midi = user_data;
+
+       if (!success) {
+               error("MIDI I/O: Failed to read initial request");
+               return;
+       }
+
+       /* request notify */
+       midi->io_cb_id =
+               bt_gatt_client_register_notify(midi->client,
+                                              midi->midi_io_handle,
+                                              midi_io_ccc_written_cb,
+                                              midi_io_value_cb,
+                                              midi,
+                                              NULL);
+}
+
+static void handle_midi_io(struct midi *midi, uint16_t value_handle)
+{
+       DBG("MIDI I/O handle: 0x%04x", value_handle);
+
+       midi->midi_io_handle = value_handle;
+
+       /*
+        * The BLE-MIDI 1.0 spec specifies that The Central shall attempt to
+        * read the MIDI I/O characteristic of the Peripheral right after
+        * estrablhishing a connection with the accessory.
+        */
+       if (!bt_gatt_client_read_value(midi->client,
+                                      value_handle,
+                                      midi_io_initial_read_cb,
+                                      midi,
+                                      NULL))
+               DBG("MIDI I/O: Failed to send request to read initial value");
+}
+
+static void handle_characteristic(struct gatt_db_attribute *attr,
+                                  void *user_data)
+{
+       struct midi *midi = user_data;
+       uint16_t value_handle;
+       bt_uuid_t uuid, midi_io_uuid;
+
+       if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL,
+                                            NULL, &uuid)) {
+               error("Failed to obtain characteristic data");
+               return;
+       }
+
+       bt_string_to_uuid(&midi_io_uuid, MIDI_IO_UUID);
+
+       if (bt_uuid_cmp(&midi_io_uuid, &uuid) == 0)
+               handle_midi_io(midi, value_handle);
+       else {
+               char uuid_str[MAX_LEN_UUID_STR];
+
+               bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+               DBG("Unsupported characteristic: %s", uuid_str);
+       }
+}
+
+static void foreach_midi_service(struct gatt_db_attribute *attr,
+                                 void *user_data)
+{
+       struct midi *midi = user_data;
+
+       gatt_db_service_foreach_char(attr, handle_characteristic, midi);
+}
+
+static int midi_device_probe(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct midi *midi;
+       char addr[18];
+
+       ba2str(device_get_address(device), addr);
+       DBG("MIDI GATT Driver profile probe (%s)", addr);
+
+       /* Ignore, if we were probed for this device already */
+       midi = btd_service_get_user_data(service);
+       if (midi) {
+               error("Profile probed twice for the same device!");
+               return -EADDRINUSE;
+       }
+
+       midi = g_new0(struct midi, 1);
+       if (!midi)
+               return -ENOMEM;
+
+       midi->dev = btd_device_ref(device);
+
+       btd_service_set_user_data(service, midi);
+
+       return 0;
+}
+
+static void midi_device_remove(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct midi *midi;
+       char addr[18];
+
+       ba2str(device_get_address(device), addr);
+       DBG("MIDI GATT Driver profile remove (%s)", addr);
+
+       midi = btd_service_get_user_data(service);
+       if (!midi) {
+               error("MIDI Service not handled by profile");
+               return;
+       }
+
+       btd_device_unref(midi->dev);
+       g_free(midi);
+}
+
+static int midi_accept(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct gatt_db *db = btd_device_get_gatt_db(device);
+       struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+       bt_uuid_t midi_uuid;
+       struct pollfd pfd;
+       struct midi *midi;
+       char addr[18];
+       char device_name[MAX_NAME_LENGTH + 11]; /* 11 = " Bluetooth\0"*/
+       int err;
+       snd_seq_client_info_t *info;
+
+       ba2str(device_get_address(device), addr);
+       DBG("MIDI GATT Driver profile accept (%s)", addr);
+
+       midi = btd_service_get_user_data(service);
+       if (!midi) {
+               error("MIDI Service not handled by profile");
+               return -ENODEV;
+       }
+
+       /* Port Name */
+       memset(device_name, 0, sizeof(device_name));
+       if (device_name_known(device))
+               device_get_name(device, device_name, sizeof(device_name));
+       else
+               strncpy(device_name, addr, sizeof(addr));
+
+       /* ALSA Sequencer Client and Port Setup */
+       err = snd_seq_open(&midi->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
+       if (err < 0) {
+               error("Could not open ALSA Sequencer: %s (%d)", snd_strerror(err), err);
+               return err;
+       }
+
+       err = snd_seq_nonblock(midi->seq_handle, SND_SEQ_NONBLOCK);
+       if (err < 0) {
+               error("Could not set nonblock mode: %s (%d)", snd_strerror(err), err);
+               goto _err_handle;
+       }
+
+       err = snd_seq_set_client_name(midi->seq_handle, device_name);
+       if (err < 0) {
+               error("Could not configure ALSA client: %s (%d)", snd_strerror(err), err);
+               goto _err_handle;
+       }
+
+       err = snd_seq_client_id(midi->seq_handle);
+       if (err < 0) {
+               error("Could retreive ALSA client: %s (%d)", snd_strerror(err), err);
+               goto _err_handle;
+       }
+       midi->seq_client_id = err;
+
+       err = snd_seq_create_simple_port(midi->seq_handle, strcat(device_name, " Bluetooth"),
+                                        SND_SEQ_PORT_CAP_READ |
+                                        SND_SEQ_PORT_CAP_WRITE |
+                                        SND_SEQ_PORT_CAP_SUBS_READ |
+                                        SND_SEQ_PORT_CAP_SUBS_WRITE,
+                                        SND_SEQ_PORT_TYPE_MIDI_GENERIC |
+                                        SND_SEQ_PORT_TYPE_HARDWARE);
+       if (err < 0) {
+               error("Could not create ALSA port: %s (%d)", snd_strerror(err), err);
+               goto _err_handle;
+       }
+       midi->seq_port_id = err;
+
+       snd_seq_client_info_alloca(&info);
+       err = snd_seq_get_client_info(midi->seq_handle, info);
+       if (err < 0)
+               goto _err_port;
+
+       /* list of relevant sequencer events */
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEOFF);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NOTEON);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_KEYPRESS);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROLLER);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PGMCHANGE);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CHANPRESS);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_PITCHBEND);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SYSEX);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_QFRAME);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGPOS);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SONGSEL);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_TUNE_REQUEST);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CLOCK);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_START);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTINUE);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_STOP);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_SENSING);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_RESET);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_CONTROL14);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_NONREGPARAM);
+       snd_seq_client_info_event_filter_add(info, SND_SEQ_EVENT_REGPARAM);
+
+       err = snd_seq_set_client_info(midi->seq_handle, info);
+       if (err < 0)
+               goto _err_port;
+
+
+       /* Input file descriptors */
+       snd_seq_poll_descriptors(midi->seq_handle, &pfd, 1, POLLIN);
+
+       midi->io = io_new(pfd.fd);
+       if (!midi->io) {
+               error("Could not allocate I/O eventloop");
+               goto _err_port;
+       }
+
+       io_set_read_handler(midi->io, midi_write_cb, midi, NULL);
+
+       midi->db = gatt_db_ref(db);
+       midi->client = bt_gatt_client_ref(client);
+
+       err = midi_read_init(&midi->midi_in);
+       if (err < 0) {
+               error("Could not initialise MIDI input parser");
+               goto _err_port;
+       }
+
+       err = midi_write_init(&midi->midi_out, bt_gatt_client_get_mtu(midi->client) - 3);
+       if (err < 0) {
+               error("Could not initialise MIDI output parser");
+               goto _err_midi;
+       }
+
+       bt_string_to_uuid(&midi_uuid, MIDI_UUID);
+       gatt_db_foreach_service(db, &midi_uuid, foreach_midi_service, midi);
+
+       btd_service_connecting_complete(service, 0);
+
+       return 0;
+
+_err_midi:
+       midi_read_free(&midi->midi_in);
+
+_err_port:
+       snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id);
+
+_err_handle:
+       snd_seq_close(midi->seq_handle);
+       midi->seq_handle = NULL;
+
+       btd_service_connecting_complete(service, err);
+
+       return err;
+}
+
+static int midi_disconnect(struct btd_service *service)
+{
+       struct btd_device *device = btd_service_get_device(service);
+       struct midi *midi;
+       char addr[18];
+
+       ba2str(device_get_address(device), addr);
+       DBG("MIDI GATT Driver profile disconnect (%s)", addr);
+
+       midi = btd_service_get_user_data(service);
+       if (!midi) {
+               error("MIDI Service not handled by profile");
+               return -ENODEV;
+       }
+
+       midi_read_free(&midi->midi_in);
+       midi_write_free(&midi->midi_out);
+       io_destroy(midi->io);
+       snd_seq_delete_simple_port(midi->seq_handle, midi->seq_port_id);
+       midi->seq_port_id = 0;
+       snd_seq_close(midi->seq_handle);
+       midi->seq_handle = NULL;
+
+       /* Clean-up any old client/db */
+       bt_gatt_client_unregister_notify(midi->client, midi->io_cb_id);
+       bt_gatt_client_unref(midi->client);
+       gatt_db_unref(midi->db);
+
+       btd_service_disconnecting_complete(service, 0);
+
+       return 0;
+}
+
+static struct btd_profile midi_profile = {
+       .name = "MIDI GATT Driver",
+       .remote_uuid = MIDI_UUID,
+       .priority = BTD_PROFILE_PRIORITY_HIGH,
+       .auto_connect = true,
+
+       .device_probe = midi_device_probe,
+       .device_remove = midi_device_remove,
+
+       .accept = midi_accept,
+
+       .disconnect = midi_disconnect,
+};
+
+static int midi_init(void)
+{
+       return btd_profile_register(&midi_profile);
+}
+
+static void midi_exit(void)
+{
+       btd_profile_unregister(&midi_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(midi, VERSION, BLUETOOTH_PLUGIN_PRIORITY_HIGH,
+                        midi_init, midi_exit);
index 86d1eb9..30cffab 100755 (executable)
@@ -616,9 +616,7 @@ static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
                        return BNEP_CONN_INVALID_DST;
                if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
                        return BNEP_CONN_INVALID_SRC;
-
-               /* Intentional no-break */
-
+               /* fall through */
        case 4: /* UUID32 */
                val = get_be32(dest);
                if (val > 0xffff)
index 19206a1..6c27007 100755 (executable)
@@ -446,9 +446,7 @@ static gboolean bnep_setup(GIOChannel *chan,
        case 16:
                if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0)
                        break;
-
-               /* Intentional no-brake */
-
+               /* fall through */
        case 4:
                val = get_be32(req->service);
                if (val > 0xffff)
diff --git a/profiles/sap/sap-u8500.c b/profiles/sap/sap-u8500.c
deleted file mode 100755 (executable)
index 5417d59..0000000
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  SAP Driver for ST-Ericsson U8500 platform
- *
- *  Copyright (C) 2010-2011 ST-Ericsson SA
- *
- *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> for
- *  ST-Ericsson.
- *
- *  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; version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <glib.h>
-#include <string.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "src/log.h"
-#include "sap.h"
-
-#define STE_SIMD_SOCK  "/dev/socket/catd_a"
-#define STE_CLIENT_TAG 0x0000
-
-#define sap_error(fmt, arg...) do { \
-       error("STE U8500 SAP: " fmt, ## arg); \
-       } while (0)
-
-#define sap_info(fmt, arg...) do { \
-       info("STE U8500 SAP: " fmt, ## arg); \
-       } while (0)
-
-struct ste_message {
-       uint16_t len;
-       uint16_t id;
-       uint32_t client_tag;
-       uint8_t payload[0];
-} __attribute__((packed));
-
-#define STE_MSG_PAYLOAD_SIZE(msg) (msg->len - sizeof(*msg) + sizeof(msg->len))
-
-enum ste_protocol {
-       STE_START_SAP_REQ       = 0x2D01,
-       STE_START_SAP_RSP       = 0x2E01,
-       STE_END_SAP_REQ         = 0x2D02,
-       STE_END_SAP_RSP         = 0x2E02,
-       STE_POWER_OFF_REQ       = 0x2D03,
-       STE_POWER_OFF_RSP       = 0x2E03,
-       STE_POWER_ON_REQ        = 0x2D04,
-       STE_POWER_ON_RSP        = 0x2E04,
-       STE_RESET_REQ           = 0x2D05,
-       STE_RESET_RSP           = 0x2E05,
-       STE_SEND_APDU_REQ       = 0x2D06,
-       STE_SEND_APDU_RSP       = 0x2E06,
-       STE_GET_ATR_REQ         = 0x2D07,
-       STE_GET_ATR_RSP         = 0x2E07,
-       STE_GET_STATUS_REQ      = 0x2D08,
-       STE_GET_STATUS_RSP      = 0x2E08,
-       STE_STATUS_IND          = 0x2F02,
-       STE_SIM_READY_IND       = 0x2F03,
-};
-
-enum ste_msg {
-       STE_SEND_APDU_MSG,
-       STE_GET_ATR_MSG,
-       STE_POWER_OFF_MSG,
-       STE_POWER_ON_MSG,
-       STE_RESET_MSG,
-       STE_GET_STATUS_MSG,
-       STE_MSG_MAX,
-};
-
-enum ste_status {
-       STE_STATUS_OK           = 0x00000000,
-       STE_STATUS_FAILURE      = 0x00000001,
-       STE_STATUS_BUSY_CALL    = 0x00000002,
-};
-
-enum ste_card_status {
-       STE_CARD_STATUS_UNKNOWN         = 0x00,
-       STE_CARD_STATUS_ACTIVE          = 0x01,
-       STE_CARD_STATUS_NOT_ACTIVE      = 0x02,
-       STE_CARD_STATUS_MISSING         = 0x03,
-       STE_CARD_STATUS_INVALID         = 0x04,
-       STE_CARD_STATUS_DISCONNECTED    = 0x05,
-};
-
-/* Card reader status bits as described in GSM 11.14, Section 12.33
- * Bits 0-2 are for card reader identity and always zeros. */
-#define ICC_READER_REMOVABLE   (1 << 3)
-#define ICC_READER_PRESENT     (1 << 4)
-#define ICC_READER_ID1         (1 << 5)
-#define ICC_READER_CARD_PRESENT        (1 << 6)
-#define ICC_READER_CARD_POWERED        (1 << 7)
-
-enum ste_state {
-       STE_DISABLED,           /* Reader not present or removed */
-       STE_POWERED_OFF,        /* Card in the reader but powered off */
-       STE_NO_CARD,            /* No card in the reader */
-       STE_ENABLED,            /* Card in the reader and powered on */
-       STE_SIM_BUSY,           /* Modem is busy with ongoing call.*/
-       STE_STATE_MAX
-};
-
-struct ste_u8500 {
-       GIOChannel *io;
-       enum ste_state state;
-       void *sap_data;
-};
-
-typedef int(*recv_state_change_cb)(void *sap, uint8_t result);
-typedef int(*recv_pdu_cb)(void *sap, uint8_t result, uint8_t *data,
-                                                               uint16_t len);
-
-static struct ste_u8500 u8500;
-
-static const uint8_t sim2sap_result[STE_MSG_MAX][STE_STATE_MAX] = {
-       /* SAP results for SEND APDU message */
-       {
-               SAP_RESULT_ERROR_NOT_ACCESSIBLE,        /* STE_DISABLED */
-               SAP_RESULT_ERROR_POWERED_OFF,           /* STE_POWERED_OFF */
-               SAP_RESULT_ERROR_CARD_REMOVED,          /* STE_NO_CARD */
-               SAP_RESULT_ERROR_NO_REASON              /* STE_ENABLED */
-       },
-
-       /* SAP results for GET_ATR message */
-       {
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_POWERED_OFF,
-               SAP_RESULT_ERROR_CARD_REMOVED,
-               SAP_RESULT_ERROR_NO_REASON
-       },
-
-       /* SAP results POWER OFF message */
-       {
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_POWERED_OFF,
-               SAP_RESULT_ERROR_CARD_REMOVED,
-               SAP_RESULT_ERROR_NO_REASON
-       },
-
-       /* SAP results POWER ON message */
-       {
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_NOT_ACCESSIBLE,
-               SAP_RESULT_ERROR_CARD_REMOVED,
-               SAP_RESULT_ERROR_POWERED_ON
-       },
-
-       /* SAP results SIM RESET message */
-       {
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_POWERED_OFF,
-               SAP_RESULT_ERROR_CARD_REMOVED,
-               SAP_RESULT_ERROR_NOT_ACCESSIBLE
-       },
-
-       /* SAP results GET STATUS message */
-       {
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_NO_REASON,
-               SAP_RESULT_ERROR_NO_REASON
-       }
-};
-
-static uint8_t get_sap_result(enum ste_msg msg, uint32_t status)
-{
-       if (!u8500.io)
-               return SAP_RESULT_ERROR_NO_REASON;
-
-       switch (status) {
-       case STE_STATUS_OK:
-               return SAP_RESULT_OK;
-
-       case STE_STATUS_FAILURE:
-               return sim2sap_result[msg][u8500.state];
-
-       default:
-               DBG("Can't convert a result (status %u)", status);
-               return SAP_RESULT_ERROR_NO_REASON;
-       }
-}
-
-static int get_sap_reader_status(uint32_t card_status, uint8_t *icc_status)
-{
-       /* Card reader is present, not removable and not ID-1 size. */
-       *icc_status = ICC_READER_PRESENT;
-
-       switch (card_status) {
-       case STE_CARD_STATUS_ACTIVE:
-               *icc_status |= ICC_READER_CARD_POWERED;
-
-       case STE_CARD_STATUS_NOT_ACTIVE:
-       case STE_CARD_STATUS_INVALID:
-               *icc_status |= ICC_READER_CARD_PRESENT;
-
-       case STE_CARD_STATUS_MISSING:
-       case STE_CARD_STATUS_DISCONNECTED:
-               return 0;
-
-       default:
-               DBG("Can't convert reader status %u", card_status);
-
-       case STE_CARD_STATUS_UNKNOWN:
-               return -1;
-       }
-}
-
-static uint8_t get_sap_status_change(uint32_t card_status)
-{
-       if (!u8500.io)
-               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
-
-       switch (card_status) {
-       case STE_CARD_STATUS_UNKNOWN:
-               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
-
-       case STE_CARD_STATUS_ACTIVE:
-               u8500.state = STE_ENABLED;
-               return SAP_STATUS_CHANGE_CARD_RESET;
-
-       case STE_CARD_STATUS_NOT_ACTIVE:
-               u8500.state = STE_DISABLED;
-               return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
-
-       case STE_CARD_STATUS_MISSING:
-               u8500.state = STE_DISABLED;
-               return SAP_STATUS_CHANGE_CARD_REMOVED;
-
-       case STE_CARD_STATUS_INVALID:
-               u8500.state = STE_DISABLED;
-               return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
-
-       default:
-               DBG("Can't convert status change %u", card_status);
-               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
-       }
-}
-
-static int send_message(GIOChannel *io, void *buf, size_t size)
-{
-       gsize written;
-
-       SAP_VDBG("io %p, size %zu", io, size);
-
-       if (g_io_channel_write_chars(io, buf, size, &written, NULL) !=
-                                                       G_IO_STATUS_NORMAL)
-               return -EIO;
-
-       return written;
-}
-
-static int send_request(GIOChannel *io, uint16_t id,
-                                               struct sap_parameter *param)
-{
-       int ret;
-       struct ste_message *msg;
-       size_t size = sizeof(*msg);
-
-       SAP_VDBG("io %p", io);
-
-       if (param)
-               size += param->len;
-
-       msg = g_try_malloc0(size);
-       if (!msg) {
-               sap_error("sending request failed: %s", strerror(ENOMEM));
-               return -ENOMEM;
-       }
-
-       msg->len = size - sizeof(msg->len);
-       msg->id = id;
-       msg->client_tag = STE_CLIENT_TAG;
-
-       if (param)
-               memcpy(msg->payload, param->val, param->len);
-
-       ret = send_message(io, msg, size);
-       if (ret < 0) {
-               sap_error("sending request failed: %s", strerror(-ret));
-       } else if (ret != (int) size) {
-               sap_error("sending request failed: %d out of %zu bytes sent",
-                                                               ret, size);
-               ret = -EIO;
-       }
-
-       g_free(msg);
-
-       return ret;
-}
-
-static void recv_status(uint32_t status)
-{
-       sap_status_ind(u8500.sap_data, get_sap_status_change(status));
-}
-
-static void recv_card_status(uint32_t status, uint8_t *param)
-{
-       uint32_t card_status;
-       uint8_t result;
-       uint8_t iccrs;
-
-       if (status != STE_STATUS_OK)
-               return;
-
-       memcpy(&card_status, param, sizeof(card_status));
-
-       if (get_sap_reader_status(card_status, &iccrs) < 0)
-               result = SAP_RESULT_ERROR_NO_REASON;
-       else
-               result = get_sap_result(STE_GET_STATUS_MSG, status);
-
-       sap_transfer_card_reader_status_rsp(u8500.sap_data, result, iccrs);
-}
-
-static void recv_state_change(uint32_t ste_msg, uint32_t status,
-                       uint32_t new_state, recv_state_change_cb callback)
-{
-       if (status != STE_STATUS_OK)
-               return;
-
-       u8500.state = new_state;
-
-       if (callback)
-               callback(u8500.sap_data, get_sap_result(ste_msg, status));
-}
-
-static void recv_pdu(uint32_t ste_msg, struct ste_message *msg, uint32_t status,
-                                       uint8_t *param, recv_pdu_cb callback)
-{
-       uint8_t *data = NULL;
-       uint8_t result;
-       int size = 0;
-
-       if (status == STE_STATUS_OK) {
-               data = param;
-               size = STE_MSG_PAYLOAD_SIZE(msg) - sizeof(status);
-       }
-
-       result = get_sap_result(ste_msg, status);
-
-       if (callback)
-               callback(u8500.sap_data, result, data, size);
-}
-
-static void simd_close(void)
-{
-       DBG("io %p", u8500.io);
-
-       if (u8500.io) {
-               g_io_channel_shutdown(u8500.io, TRUE, NULL);
-               g_io_channel_unref(u8500.io);
-       }
-
-       u8500.state = STE_DISABLED;
-       u8500.io = NULL;
-       u8500.sap_data = NULL;
-}
-
-static void recv_sim_ready(void)
-{
-       sap_info("sim is ready. Try to connect again");
-
-       if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
-               sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED);
-               simd_close();
-       }
-}
-
-static void recv_connect_rsp(uint32_t status)
-{
-       switch (status) {
-       case STE_STATUS_OK:
-               if (u8500.state != STE_SIM_BUSY)
-                       sap_connect_rsp(u8500.sap_data, SAP_STATUS_OK);
-               break;
-       case STE_STATUS_BUSY_CALL:
-               if (u8500.state != STE_SIM_BUSY) {
-                       sap_connect_rsp(u8500.sap_data,
-                                               SAP_STATUS_OK_ONGOING_CALL);
-
-                       u8500.state = STE_SIM_BUSY;
-               }
-               break;
-       default:
-               sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED);
-               simd_close();
-               break;
-       }
-}
-
-static void recv_response(struct ste_message *msg)
-{
-       uint32_t status;
-       uint8_t *param;
-
-       SAP_VDBG("msg_id 0x%x", msg->id);
-
-       if (msg->id == STE_END_SAP_RSP) {
-               sap_disconnect_rsp(u8500.sap_data);
-               simd_close();
-               return;
-       }
-
-       param = msg->payload;
-       memcpy(&status, param, sizeof(status));
-       param += sizeof(status);
-
-       SAP_VDBG("status 0x%x", status);
-
-       switch (msg->id) {
-       case STE_START_SAP_RSP:
-               recv_connect_rsp(status);
-               break;
-       case STE_SEND_APDU_RSP:
-               recv_pdu(STE_SEND_APDU_MSG, msg, status, param,
-                                                       sap_transfer_apdu_rsp);
-               break;
-
-       case STE_GET_ATR_RSP:
-               recv_pdu(STE_GET_ATR_MSG, msg, status, param,
-                                                       sap_transfer_atr_rsp);
-               break;
-
-       case STE_POWER_OFF_RSP:
-               recv_state_change(STE_POWER_OFF_MSG, status, STE_POWERED_OFF,
-                                                       sap_power_sim_off_rsp);
-               break;
-
-       case STE_POWER_ON_RSP:
-               recv_state_change(STE_POWER_ON_MSG, status, STE_ENABLED,
-                                                       sap_power_sim_on_rsp);
-               break;
-
-       case STE_RESET_RSP:
-               recv_state_change(STE_RESET_MSG, status, STE_ENABLED,
-                                                       sap_reset_sim_rsp);
-               break;
-
-       case STE_GET_STATUS_RSP:
-               recv_card_status(status, param);
-               break;
-
-       case STE_STATUS_IND:
-               recv_status(status);
-               break;
-
-       case STE_SIM_READY_IND:
-               recv_sim_ready();
-               break;
-
-       default:
-               sap_error("unsupported message received (id 0x%x)", msg->id);
-       }
-}
-
-static int recv_message(void *buf, size_t size)
-{
-       uint8_t *iter = buf;
-       struct ste_message *msg = buf;
-
-       do {
-               SAP_VDBG("size %zu msg->len %u.", size, msg->len);
-
-               if (size < sizeof(*msg)) {
-                       sap_error("invalid message received (%zu bytes)", size);
-                       return -EBADMSG;
-               }
-
-               /* Message must be complete. */
-               if (size < (msg->len + sizeof(msg->len))) {
-                       sap_error("incomplete message received (%zu bytes)",
-                                                                       size);
-                       return -EBADMSG;
-               }
-
-               recv_response(msg);
-
-               /* Reduce total buffer size by just handled frame size. */
-               size -= msg->len + sizeof(msg->len);
-
-               /* Move msg pointer to the next message if any. */
-               iter += msg->len + sizeof(msg->len);
-               msg = (struct ste_message *)iter;
-       } while (size > 0);
-
-       return 0;
-}
-
-static gboolean simd_data_cb(GIOChannel *io, GIOCondition cond, gpointer data)
-{
-       char buf[SAP_BUF_SIZE];
-       gsize bytes_read;
-       GIOStatus gstatus;
-
-       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
-               DBG("Error condition on sim socket (0x%x)", cond);
-               return FALSE;
-       }
-
-       gstatus = g_io_channel_read_chars(io, buf, sizeof(buf), &bytes_read,
-                                                                       NULL);
-       if (gstatus != G_IO_STATUS_NORMAL) {
-               sap_error("error while reading from channel (%d)", gstatus);
-               return TRUE;
-       }
-
-       if (recv_message(buf, bytes_read) < 0)
-               sap_error("error while parsing STE Sim message");
-
-       return TRUE;
-}
-
-static void simd_watch(int sock, void *sap_data)
-{
-       GIOChannel *io;
-
-       DBG("sock %d, sap_data %p ", sock, sap_data);
-
-       io = g_io_channel_unix_new(sock);
-
-       g_io_channel_set_close_on_unref(io, TRUE);
-       g_io_channel_set_encoding(io, NULL, NULL);
-       g_io_channel_set_buffered(io, FALSE);
-
-       u8500.io = io;
-       u8500.sap_data = sap_data;
-       u8500.state = STE_DISABLED;
-
-       g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
-                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                       simd_data_cb, NULL, NULL);
-}
-
-static int simd_connect(void *sap_data)
-{
-       struct sockaddr_un addr;
-       int sock;
-       int err;
-
-       /* Already connected to simd */
-       if (u8500.io)
-               return -EALREADY;
-
-       sock = socket(PF_UNIX, SOCK_STREAM, 0);
-       if (sock < 0) {
-               err = -errno;
-               sap_error("creating socket failed: %s", strerror(-err));
-               return err;
-       }
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       memcpy(addr.sun_path, STE_SIMD_SOCK, sizeof(STE_SIMD_SOCK) - 1);
-
-       if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               err = -errno;
-               sap_error("connect to the socket failed: %s", strerror(-err));
-               goto failed;
-       }
-
-       if (fcntl(sock, F_SETFL, O_NONBLOCK) > 0) {
-               err = -errno;
-               sap_error("setting up socket failed: %s", strerror(-err));
-               goto failed;
-       }
-
-       simd_watch(sock, sap_data);
-
-       return 0;
-
-failed:
-       close(sock);
-       return err;
-}
-
-void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
-{
-       DBG("sap_device %p maxmsgsize %u", sap_device, maxmsgsize);
-
-       sap_info("connect request");
-
-       if (simd_connect(sap_device) < 0) {
-               sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
-               sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED);
-               simd_close();
-       }
-}
-
-void sap_disconnect_req(void *sap_device, uint8_t linkloss)
-{
-       DBG("sap_device %p linkloss %u", sap_device, linkloss);
-
-       sap_info("disconnect request %s", linkloss ? "by link loss" : "");
-
-       if (u8500.state == STE_DISABLED) {
-               sap_disconnect_rsp(sap_device);
-               simd_close();
-               return;
-       }
-
-       if (linkloss) {
-               simd_close();
-               return;
-       }
-
-       if (send_request(u8500.io, STE_END_SAP_REQ, NULL) < 0) {
-               sap_disconnect_rsp(sap_device);
-               return;
-       }
-}
-
-void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
-{
-       uint8_t result;
-
-       SAP_VDBG("sap_device %p param %p", sap_device, param);
-
-       if (u8500.state != STE_ENABLED) {
-               result = get_sap_result(STE_SEND_APDU_MSG, STE_STATUS_FAILURE);
-               sap_transfer_apdu_rsp(sap_device, result, NULL, 0);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_SEND_APDU_REQ, param) < 0)
-               sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA,
-                                                               NULL, 0);
-}
-
-void sap_transfer_atr_req(void *sap_device)
-{
-       uint8_t result;
-
-       DBG("sap_device %p", sap_device);
-
-       if (u8500.state != STE_ENABLED) {
-               result = get_sap_result(STE_GET_ATR_MSG, STE_STATUS_FAILURE);
-               sap_transfer_atr_rsp(sap_device, result, NULL, 0);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_GET_ATR_REQ, NULL) < 0)
-               sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA, NULL,
-                                                                       0);
-}
-
-void sap_power_sim_off_req(void *sap_device)
-{
-       uint8_t result;
-
-       DBG("sap_device %p", sap_device);
-
-       if (u8500.state != STE_ENABLED) {
-               result = get_sap_result(STE_POWER_OFF_MSG, STE_STATUS_FAILURE);
-               sap_power_sim_off_rsp(sap_device, result);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_POWER_OFF_REQ, NULL) < 0)
-               sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_power_sim_on_req(void *sap_device)
-{
-       uint8_t result;
-
-       DBG("sap_device %p", sap_device);
-
-       if (u8500.state != STE_POWERED_OFF) {
-               result = get_sap_result(STE_POWER_ON_MSG, STE_STATUS_FAILURE);
-               sap_power_sim_on_rsp(sap_device, result);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_POWER_ON_REQ, NULL) < 0)
-               sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_reset_sim_req(void *sap_device)
-{
-       uint8_t result;
-
-       DBG("sap_device %p", sap_device);
-
-       if (u8500.state != STE_ENABLED) {
-               result = get_sap_result(STE_RESET_MSG, STE_STATUS_FAILURE);
-               sap_reset_sim_rsp(sap_device, result);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_RESET_REQ, NULL) < 0)
-               sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
-}
-
-void sap_transfer_card_reader_status_req(void *sap_device)
-{
-       uint8_t result;
-
-       DBG("sap_device %p", sap_device);
-
-       if (u8500.state == STE_DISABLED) {
-               result = get_sap_result(STE_GET_STATUS_MSG, STE_STATUS_FAILURE);
-               sap_transfer_card_reader_status_rsp(sap_device, result, 0);
-               return;
-       }
-
-       if (send_request(u8500.io, STE_GET_STATUS_REQ, NULL) < 0)
-               sap_transfer_card_reader_status_rsp(sap_device,
-                                               SAP_RESULT_ERROR_NO_DATA, 0);
-}
-
-void sap_set_transport_protocol_req(void *sap_device,
-                                               struct sap_parameter *param)
-{
-       DBG("sap_device %p", sap_device);
-
-       sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
-}
-
-int sap_init(void)
-{
-       u8500.state = STE_DISABLED;
-       info("STE U8500 SAP driver initialized");
-       return 0;
-}
-
-void sap_exit(void)
-{
-}
index ce423a1..5de682a 100755 (executable)
@@ -674,8 +674,7 @@ int sap_connect_rsp(void *sap_device, uint8_t status)
                param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
                put_be16(SAP_BUF_SIZE, &param->val);
                size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
-
-               /* fall */
+               /* fall through */
        default:
                conn->state = SAP_STATE_DISCONNECTED;
 
@@ -992,7 +991,7 @@ int sap_status_ind(void *sap_device, uint8_t status_change)
 
                /* Change state to connected after ongoing call ended */
                sap_set_connected(server);
-               /* fall */
+               /* fall through */
        case SAP_STATE_CONNECTED:
        case SAP_STATE_GRACEFUL_DISCONNECT:
                memset(buf, 0, sizeof(buf));
index 0ff4a43..9e8f577 100755 (executable)
@@ -192,7 +192,7 @@ static int scan_param_accept(struct btd_service *service)
        }
 
        scan->db = gatt_db_ref(db);
-       scan->client = bt_gatt_client_ref(client);
+       scan->client = bt_gatt_client_clone(client);
 
        bt_string_to_uuid(&scan_parameters_uuid, SCAN_PARAMETERS_UUID);
        gatt_db_foreach_service(db, &scan_parameters_uuid,
index c7fc227..8adbad1 100644 (file)
@@ -178,10 +178,12 @@ struct discovery_filter {
        uint16_t pathloss;
        int16_t rssi;
        GSList *uuids;
+       bool duplicate;
 };
 
 struct watch_client {
        struct btd_adapter *adapter;
+       DBusMessage *msg;
        char *owner;
        guint watch;
        struct discovery_filter *discovery_filter;
@@ -228,7 +230,7 @@ struct btd_adapter {
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        bdaddr_t le_static_addr;
 #endif
-       int8_t bdaddr_type;             /* address type */
+       uint8_t bdaddr_type;            /* address type */
        uint32_t dev_class;             /* controller class of device */
        char *name;                     /* controller device name */
        char *short_name;               /* controller short name */
@@ -236,6 +238,7 @@ struct btd_adapter {
        uint32_t current_settings;      /* current controller settings */
 
        char *path;                     /* adapter object path */
+       uint16_t manufacturer;          /* adapter manufacturer */
        uint8_t major_class;            /* configured major class */
        uint8_t minor_class;            /* configured minor class */
        char *system_name;              /* configured system name */
@@ -341,6 +344,11 @@ struct btd_adapter {
        bool is_default;                /* true if adapter is default one */
 };
 
+typedef enum {
+       ADAPTER_AUTHORIZE_DISCONNECTED = 0,
+       ADAPTER_AUTHORIZE_CHECK_CONNECTED
+} adapter_authorize_type;
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 enum {
        DEINIT_6LOWPAN,
@@ -508,6 +516,20 @@ static uint8_t get_mode(const char *mode)
                return MODE_UNKNOWN;
 }
 
+static const char *adapter_dir(struct btd_adapter *adapter)
+{
+       static char dir[25];
+
+       if (adapter->bdaddr_type == BDADDR_LE_RANDOM) {
+               strcpy(dir, "static-");
+               ba2str(&adapter->bdaddr, dir + 7);
+       } else {
+               ba2str(&adapter->bdaddr, dir);
+       }
+
+       return dir;
+}
+
 uint8_t btd_adapter_get_address_type(struct btd_adapter *adapter)
 {
        return adapter->bdaddr_type;
@@ -517,7 +539,6 @@ static void store_adapter_info(struct btd_adapter *adapter)
 {
        GKeyFile *key_file;
        char filename[PATH_MAX];
-       char address[18];
        char *str;
        gsize length = 0;
        gboolean discoverable;
@@ -552,8 +573,9 @@ static void store_adapter_info(struct btd_adapter *adapter)
        else
                g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "source");
 #endif
-       ba2str(&adapter->bdaddr, address);
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings",
+                                               adapter_dir(adapter));
 
        create_file(filename, S_IRUSR | S_IWUSR);
 
@@ -1015,7 +1037,8 @@ static void trigger_pairable_timeout(struct btd_adapter *adapter)
                return;
 
        if (adapter->pairable_timeout > 0)
-               g_timeout_add_seconds(adapter->pairable_timeout,
+               adapter->pairable_timeout_id =
+                       g_timeout_add_seconds(adapter->pairable_timeout,
                                        pairable_timeout_handler, adapter);
 }
 
@@ -1160,7 +1183,6 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        bool exact_match = false;
 #endif
-       char addr_str[18];
 
        if (!adapter)
                return NULL;
@@ -1168,23 +1190,17 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
        bacpy(&addr.bdaddr, dst);
        addr.bdaddr_type = bdaddr_type;
 
-       ba2str(dst, addr_str);
-
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        list = g_slist_find_custom(adapter->devices, &addr,
                                                device_addr_type_strict_cmp);
        if (list) {
                device = list->data;
                exact_match = true;
-       } else {
-#endif
-               list = g_slist_find_custom(adapter->devices, &addr,
-                                                       device_addr_type_cmp);
-               if (list) {
-                       device = list->data;
-               }
        }
-
+#else
+       list = g_slist_find_custom(adapter->devices, &addr,
+                                                       device_addr_type_cmp);
+#endif
        if (!list)
                return NULL;
 
@@ -1672,11 +1688,6 @@ static void passive_scanning_complete(uint8_t status, uint16_t length,
        struct btd_adapter *adapter = user_data;
        const struct mgmt_cp_start_discovery *rp = param;
 
-       if (!rp) {
-               error("Error ocurred in Scanning, rp is NULL");
-               return;
-       }
-
        DBG("status 0x%02x", status);
 
        if (length < sizeof(*rp)) {
@@ -1902,16 +1913,37 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
        struct btd_adapter *adapter = user_data;
+       struct watch_client *client = adapter->discovery_list->data;
        const struct mgmt_cp_start_discovery *rp = param;
+       DBusMessage *reply;
 
        DBG("status 0x%02x", status);
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
-       DBG("Discovery Type 0x%02x", rp->type);
+       DBG("status 0x%02x", status);
 #endif
 
+       /* Is there are no clients the discovery must have been stopped while
+        * discovery command was pending.
+        */
+       if (!client) {
+               struct mgmt_cp_stop_discovery cp;
+
+               if (status != MGMT_STATUS_SUCCESS)
+                       return;
+
+               /* Stop discovering as there are no clients left */
+               cp.type = rp->type;
+               mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+                                       adapter->dev_id, sizeof(cp), &cp,
+                                       NULL, NULL, NULL);
+               return;
+       }
+
        if (length < sizeof(*rp)) {
                btd_error(adapter->dev_id,
                        "Wrong size of start discovery return parameters");
+               if (client->msg)
+                       goto fail;
                return;
        }
 
@@ -1929,6 +1961,14 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
                else
                        adapter->filtered_discovery = false;
 
+               if (client->msg) {
+                       reply = g_dbus_create_reply(client->msg,
+                                                       DBUS_TYPE_INVALID);
+                       g_dbus_send_message(dbus_conn, reply);
+                       dbus_message_unref(client->msg);
+                       client->msg = NULL;
+               }
+
                if (adapter->discovering)
                        return;
 
@@ -1936,14 +1976,21 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
                g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                        ADAPTER_INTERFACE, "Discovering");
                return;
-
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        } else {
                adapter->discovering = false;
                g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                        ADAPTER_INTERFACE, "Discovering");
 #endif
+       }
 
+fail:
+       /* Reply with an error if the first discovery has failed */
+       if (client->msg) {
+               reply = btd_error_busy(client->msg);
+               g_dbus_send_message(dbus_conn, reply);
+               g_dbus_remove_watch(dbus_conn, client->watch);
+               return;
        }
 
        /*
@@ -2367,6 +2414,114 @@ static void discovering_callback(uint16_t index, uint16_t length,
        }
 }
 
+static void invalidate_rssi_and_tx_power(gpointer a)
+{
+       struct btd_device *dev = a;
+
+       device_set_rssi(dev, 0);
+       device_set_tx_power(dev, 127);
+}
+
+static gboolean remove_temp_devices(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       GSList *l, *next;
+
+       DBG("%s", adapter->path);
+
+       adapter->temp_devices_timeout = 0;
+
+       for (l = adapter->devices; l != NULL; l = next) {
+               struct btd_device *dev = l->data;
+
+               next = g_slist_next(l);
+
+               if (device_is_temporary(dev) && !btd_device_is_connected(dev))
+                       btd_adapter_remove_device(adapter, dev);
+       }
+
+       return FALSE;
+}
+
+static void discovery_cleanup(struct btd_adapter *adapter)
+{
+       GSList *l, *next;
+
+       adapter->discovery_type = 0x00;
+
+       if (adapter->discovery_idle_timeout > 0) {
+               g_source_remove(adapter->discovery_idle_timeout);
+               adapter->discovery_idle_timeout = 0;
+       }
+
+       if (adapter->temp_devices_timeout > 0) {
+               g_source_remove(adapter->temp_devices_timeout);
+               adapter->temp_devices_timeout = 0;
+       }
+
+       g_slist_free_full(adapter->discovery_found,
+                                               invalidate_rssi_and_tx_power);
+       adapter->discovery_found = NULL;
+
+       if (!adapter->devices)
+               return;
+
+       for (l = adapter->devices; l != NULL; l = next) {
+               struct btd_device *dev = l->data;
+
+               next = g_slist_next(l);
+
+               if (device_is_temporary(dev) && !device_is_connectable(dev))
+                       btd_adapter_remove_device(adapter, dev);
+       }
+
+       adapter->temp_devices_timeout = g_timeout_add_seconds(TEMP_DEV_TIMEOUT,
+                                               remove_temp_devices, adapter);
+}
+
+static void discovery_free(void *user_data)
+{
+       struct watch_client *client = user_data;
+
+       if (client->watch)
+               g_dbus_remove_watch(dbus_conn, client->watch);
+
+       if (client->discovery_filter) {
+               free_discovery_filter(client->discovery_filter);
+               client->discovery_filter = NULL;
+       }
+
+       if (client->msg)
+               dbus_message_unref(client->msg);
+
+       g_free(client->owner);
+       g_free(client);
+}
+
+static void discovery_remove(struct watch_client *client)
+{
+       struct btd_adapter *adapter = client->adapter;
+
+       DBG("owner %s", client->owner);
+
+       adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
+                       client);
+
+       adapter->discovery_list = g_slist_remove(adapter->discovery_list,
+                       client);
+
+       discovery_free(client);
+
+       /*
+        * If there are other client discoveries in progress, then leave
+        * it active. If not, then make sure to stop the restart timeout.
+        */
+       if (adapter->discovery_list)
+               return;
+
+       discovery_cleanup(adapter);
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void stop_discovery_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
@@ -2415,27 +2570,42 @@ static void stop_le_discovery_complete(uint8_t status, uint16_t length,
                }
        }
 }
-
 #else
 
 static void stop_discovery_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
-       struct btd_adapter *adapter = user_data;
+       struct watch_client *client = user_data;
+       struct btd_adapter *adapter = client->adapter;
+       DBusMessage *reply;
 
        DBG("status 0x%02x", status);
 
-       if (status == MGMT_STATUS_SUCCESS) {
-               adapter->discovery_type = 0x00;
-               adapter->discovery_enable = 0x00;
-               adapter->filtered_discovery = false;
-               adapter->no_scan_restart_delay = false;
-               adapter->discovering = false;
-               g_dbus_emit_property_changed(dbus_conn, adapter->path,
-                                       ADAPTER_INTERFACE, "Discovering");
+       if (status != MGMT_STATUS_SUCCESS) {
+               if (client->msg) {
+                       reply = btd_error_busy(client->msg);
+                       g_dbus_send_message(dbus_conn, reply);
+               }
+               goto done;
+       }
 
-               trigger_passive_scanning(adapter);
+       if (client->msg) {
+               reply = g_dbus_create_reply(client->msg, DBUS_TYPE_INVALID);
+               g_dbus_send_message(dbus_conn, reply);
        }
+
+       adapter->discovery_type = 0x00;
+       adapter->discovery_enable = 0x00;
+       adapter->filtered_discovery = false;
+       adapter->no_scan_restart_delay = false;
+       adapter->discovering = false;
+       g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering");
+
+       trigger_passive_scanning(adapter);
+
+done:
+       discovery_remove(client);
 }
 #endif
 
@@ -2447,42 +2617,6 @@ static int compare_sender(gconstpointer a, gconstpointer b)
        return g_strcmp0(client->owner, sender);
 }
 
-static void invalidate_rssi_and_tx_power(gpointer a)
-{
-       struct btd_device *dev = a;
-
-       device_set_rssi(dev, 0);
-       device_set_tx_power(dev, 127);
-}
-
-static void discovery_cleanup(struct btd_adapter *adapter)
-{
-       g_slist_free_full(adapter->discovery_found,
-                                               invalidate_rssi_and_tx_power);
-       adapter->discovery_found = NULL;
-}
-
-static gboolean remove_temp_devices(gpointer user_data)
-{
-       struct btd_adapter *adapter = user_data;
-       GSList *l, *next;
-
-       DBG("%s", adapter->path);
-
-       adapter->temp_devices_timeout = 0;
-
-       for (l = adapter->devices; l != NULL; l = next) {
-               struct btd_device *dev = l->data;
-
-               next = g_slist_next(l);
-
-               if (device_is_temporary(dev) && !btd_device_is_connected(dev))
-                       btd_adapter_remove_device(adapter, dev);
-       }
-
-       return FALSE;
-}
-
 static gint g_strcmp(gconstpointer a, gconstpointer b)
 {
        return strcmp(a, b);
@@ -2662,7 +2796,7 @@ static bool filters_equal(struct mgmt_cp_start_service_discovery *a,
        return true;
 }
 
-static void update_discovery_filter(struct btd_adapter *adapter)
+static int update_discovery_filter(struct btd_adapter *adapter)
 {
        struct mgmt_cp_start_service_discovery *sd_cp;
 
@@ -2671,7 +2805,7 @@ static void update_discovery_filter(struct btd_adapter *adapter)
        if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) {
                btd_error(adapter->dev_id,
                                "discovery_filter_to_mgmt_cp returned error");
-               return;
+               return -ENOMEM;
        }
 
        /*
@@ -2682,13 +2816,56 @@ static void update_discovery_filter(struct btd_adapter *adapter)
            adapter->discovering != 0) {
                DBG("filters were equal, deciding to not restart the scan.");
                g_free(sd_cp);
-               return;
+               return 0;
        }
 
        g_free(adapter->current_discovery_filter);
        adapter->current_discovery_filter = sd_cp;
 
        trigger_start_discovery(adapter, 0);
+
+       return -EINPROGRESS;
+}
+
+static int discovery_stop(struct watch_client *client)
+{
+       struct btd_adapter *adapter = client->adapter;
+       struct mgmt_cp_stop_discovery cp;
+
+       /* Check if there are more client discovering */
+       if (g_slist_next(adapter->discovery_list)) {
+               discovery_remove(client);
+               update_discovery_filter(adapter);
+               return 0;
+       }
+
+       /*
+        * In the idle phase of a discovery, there is no need to stop it
+        * and so it is enough to send out the signal and just return.
+        */
+       if (adapter->discovery_enable == 0x00) {
+               discovery_remove(client);
+               adapter->discovering = false;
+               g_dbus_emit_property_changed(dbus_conn, adapter->path,
+                               ADAPTER_INTERFACE, "Discovering");
+
+               trigger_passive_scanning(adapter);
+
+               return 0;
+       }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+        adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
+       cp.type = adapter->discovery_type;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+        cp.type = 0x01;
+#endif
+       mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+                       adapter->dev_id, sizeof(cp), &cp,
+                       stop_discovery_complete, client, NULL);
+
+       return -EINPROGRESS;
 }
 
 static void discovery_destroy(void *user_data)
@@ -2744,47 +2921,10 @@ static void discovery_destroy(void *user_data)
 static void discovery_disconnect(DBusConnection *conn, void *user_data)
 {
        struct watch_client *client = user_data;
-       struct btd_adapter *adapter = client->adapter;
-       struct mgmt_cp_stop_discovery cp;
 
        DBG("owner %s", client->owner);
 
-       adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
-                                                               client);
-
-       adapter->discovery_list = g_slist_remove(adapter->discovery_list,
-                                                               client);
-
-       /*
-        * There is no need for extra cleanup of the client since that
-        * will be done by the destroy callback.
-        *
-        * However in case this is the last client, the discovery in
-        * the kernel needs to be disabled.
-        */
-       if (adapter->discovery_list) {
-               update_discovery_filter(adapter);
-               return;
-       }
-
-       /*
-        * In the idle phase of a discovery, there is no need to stop it
-        * and so it is enough to send out the signal and just return.
-        */
-       if (adapter->discovery_enable == 0x00) {
-               adapter->discovering = false;
-               g_dbus_emit_property_changed(dbus_conn, adapter->path,
-                                       ADAPTER_INTERFACE, "Discovering");
-
-               trigger_passive_scanning(adapter);
-               return;
-       }
-
-       cp.type = adapter->discovery_type;
-
-       mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
-                               adapter->dev_id, sizeof(cp), &cp,
-                               stop_discovery_complete, adapter, NULL);
+       discovery_stop(client);
 }
 
 /*
@@ -5622,6 +5762,7 @@ static DBusMessage *start_discovery(DBusConnection *conn,
        const char *sender = dbus_message_get_sender(msg);
        struct watch_client *client;
        bool is_discovering;
+       int err;
 
        DBG("sender %s", sender);
 
@@ -5642,12 +5783,14 @@ static DBusMessage *start_discovery(DBusConnection *conn,
         * and trigger scan.
         */
        if (client) {
+               if (client->msg)
+                       return btd_error_busy(msg);
+
                adapter->set_filter_list = g_slist_remove(
                                             adapter->set_filter_list, client);
                adapter->discovery_list = g_slist_prepend(
                                              adapter->discovery_list, client);
-               update_discovery_filter(adapter);
-               return dbus_message_new_method_return(msg);
+               goto done;
        }
 
        client = g_new0(struct watch_client, 1);
@@ -5657,25 +5800,30 @@ static DBusMessage *start_discovery(DBusConnection *conn,
        client->discovery_filter = NULL;
        client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
                                                discovery_disconnect, client,
-                                               discovery_destroy);
+                                               NULL);
        adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
                                                                client);
 
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
-#endif
-
+done:
        /*
         * Just trigger the discovery here. In case an already running
         * discovery in idle phase exists, it will be restarted right
         * away.
         */
-       update_discovery_filter(adapter);
+       err = update_discovery_filter(adapter);
+       if (!err)
+               return dbus_message_new_method_return(msg);
 
-       return dbus_message_new_method_return(msg);
+       /* If the discovery has to be started wait it complete to reply */
+       if (err == -EINPROGRESS) {
+               client->msg = dbus_message_ref(msg);
+               return NULL;
+       }
+
+       return btd_error_failed(msg, strerror(-err));
 }
 
-static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+static bool parse_uuids(DBusMessageIter *value, struct discovery_filter *filter)
 {
        DBusMessageIter arriter;
 
@@ -5700,7 +5848,7 @@ static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
                bt_uuid_to_uuid128(&uuid, &u128);
                bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
 
-               *uuids = g_slist_prepend(*uuids, strdup(uuidstr));
+               filter->uuids = g_slist_prepend(filter->uuids, strdup(uuidstr));
 
                dbus_message_iter_next(&arriter);
        }
@@ -5708,33 +5856,35 @@ static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
        return true;
 }
 
-static bool parse_rssi(DBusMessageIter *value, int16_t *rssi)
+static bool parse_rssi(DBusMessageIter *value, struct discovery_filter *filter)
 {
        if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_INT16)
                return false;
 
-       dbus_message_iter_get_basic(value, rssi);
+       dbus_message_iter_get_basic(value, &filter->rssi);
        /* -127 <= RSSI <= +20 (spec V4.2 [Vol 2, Part E] 7.7.65.2) */
-       if (*rssi > 20 || *rssi < -127)
+       if (filter->rssi > 20 || filter->rssi < -127)
                return false;
 
        return true;
 }
 
-static bool parse_pathloss(DBusMessageIter *value, uint16_t *pathloss)
+static bool parse_pathloss(DBusMessageIter *value,
+                               struct discovery_filter *filter)
 {
        if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
                return false;
 
-       dbus_message_iter_get_basic(value, pathloss);
+       dbus_message_iter_get_basic(value, &filter->pathloss);
        /* pathloss filter must be smaller that PATHLOSS_MAX */
-       if (*pathloss > PATHLOSS_MAX)
+       if (filter->pathloss > PATHLOSS_MAX)
                return false;
 
        return true;
 }
 
-static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
+static bool parse_transport(DBusMessageIter *value,
+                                       struct discovery_filter *filter)
 {
        char *transport_str;
 
@@ -5744,31 +5894,47 @@ static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
        dbus_message_iter_get_basic(value, &transport_str);
 
        if (!strcmp(transport_str, "bredr"))
-               *transport = SCAN_TYPE_BREDR;
+               filter->type = SCAN_TYPE_BREDR;
        else if (!strcmp(transport_str, "le"))
-               *transport = SCAN_TYPE_LE;
-       else if (!strcmp(transport_str, "auto"))
-               *transport = SCAN_TYPE_DUAL;
-       else
+               filter->type = SCAN_TYPE_LE;
+       else if (strcmp(transport_str, "auto"))
                return false;
 
        return true;
 }
 
-static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
-                                               struct discovery_filter *filter)
+static bool parse_duplicate_data(DBusMessageIter *value,
+                                       struct discovery_filter *filter)
 {
-       if (!strcmp("UUIDs", key))
-               return parse_uuids(value, &filter->uuids);
+       if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+               return false;
 
-       if (!strcmp("RSSI", key))
-               return parse_rssi(value, &filter->rssi);
+       dbus_message_iter_get_basic(value, &filter->duplicate);
 
-       if (!strcmp("Pathloss", key))
-               return parse_pathloss(value, &filter->pathloss);
+       return true;
+}
 
-       if (!strcmp("Transport", key))
-               return parse_transport(value, &filter->type);
+struct filter_parser {
+       const char *name;
+       bool (*func)(DBusMessageIter *iter, struct discovery_filter *filter);
+} parsers[] = {
+       { "UUIDs", parse_uuids },
+       { "RSSI", parse_rssi },
+       { "Pathloss", parse_pathloss },
+       { "Transport", parse_transport },
+       { "DuplicateData", parse_duplicate_data },
+       { }
+};
+
+static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
+                                               struct discovery_filter *filter)
+{
+       struct filter_parser *parser;
+
+       for (parser = parsers; parser && parser->name; parser++) {
+               if (!strcmp(parser->name, key))
+                       return parser->func(value, filter);
+       }
 
        DBG("Unknown key parameter: %s!\n", key);
        return false;
@@ -5795,6 +5961,7 @@ static bool parse_discovery_filter_dict(struct btd_adapter *adapter,
        (*filter)->pathloss = DISTANCE_VAL_INVALID;
        (*filter)->rssi = DISTANCE_VAL_INVALID;
        (*filter)->type = get_scan_type(adapter);
+       (*filter)->duplicate = false;
 
        dbus_message_iter_init(msg, &iter);
        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
@@ -5839,8 +6006,9 @@ static bool parse_discovery_filter_dict(struct btd_adapter *adapter,
            (*filter)->rssi != DISTANCE_VAL_INVALID)
                goto invalid_args;
 
-       DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d",
-           (*filter)->type, (*filter)->rssi, (*filter)->pathloss);
+       DBG("filtered discovery params: transport: %d rssi: %d pathloss: %d "
+               " duplicate data: %s ", (*filter)->type, (*filter)->rssi,
+               (*filter)->pathloss, (*filter)->duplicate ? "true" : "false");
 
        return true;
 
@@ -5888,8 +6056,7 @@ static DBusMessage *set_discovery_filter(DBusConnection *conn,
                adapter->set_filter_list = g_slist_remove(
                                              adapter->set_filter_list,
                                              client);
-               g_free(client->owner);
-               g_free(client);
+               discovery_free(client);
                DBG("successfully cleared pre-set filter");
        } else if (discovery_filter) {
                /* Client pre-setting his filter for first time */
@@ -5899,7 +6066,7 @@ static DBusMessage *set_discovery_filter(DBusConnection *conn,
                client->discovery_filter = discovery_filter;
                client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
                                                discovery_disconnect, client,
-                                               discovery_destroy);
+                                               NULL);
                adapter->set_filter_list = g_slist_prepend(
                                             adapter->set_filter_list, client);
 
@@ -5914,9 +6081,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
 {
        struct btd_adapter *adapter = user_data;
        const char *sender = dbus_message_get_sender(msg);
-       struct mgmt_cp_stop_discovery cp;
        struct watch_client *client;
        GSList *list;
+       int err;
 
        DBG("sender %s", sender);
 
@@ -5924,49 +6091,25 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
                return btd_error_not_ready(msg);
 
        list = g_slist_find_custom(adapter->discovery_list, sender,
-                                               compare_sender);
+                       compare_sender);
        if (!list)
                return btd_error_failed(msg, "No discovery started");
 
        client = list->data;
 
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
-#endif
-       cp.type = adapter->discovery_type;
-
-       /*
-        * The destroy function will cleanup the client information and
-        * also remove it from the list of discovery clients.
-        */
-       g_dbus_remove_watch(dbus_conn, client->watch);
-
-       if (adapter->discovery_list) {
-               update_discovery_filter(adapter);
-               return dbus_message_new_method_return(msg);
-       }
-
-       /*
-        * In the idle phase of a discovery, there is no need to stop it
-        * and so it is enough to send out the signal and just return.
-        */
-       if (adapter->discovery_enable == 0x00) {
-               adapter->discovering = false;
-               g_dbus_emit_property_changed(dbus_conn, adapter->path,
-                                       ADAPTER_INTERFACE, "Discovering");
-
-               trigger_passive_scanning(adapter);
+       if (client->msg)
+               return btd_error_busy(msg);
 
-               return dbus_message_new_method_return(msg);
+       err = discovery_stop(client);
+       switch (err) {
+               case 0:
+                       return dbus_message_new_method_return(msg);
+               case -EINPROGRESS:
+                       client->msg = dbus_message_ref(msg);
+                       return NULL;
+               default:
+                       return btd_error_failed(msg, strerror(-err));
        }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       cp.type = 0x01;
-#endif
-       mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
-                               adapter->dev_id, sizeof(cp), &cp,
-                               stop_discovery_complete, adapter, NULL);
-
-       return dbus_message_new_method_return(msg);
 }
 
 static gboolean property_get_address(const GDBusPropertyTable *property,
@@ -5983,6 +6126,23 @@ static gboolean property_get_address(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+static gboolean property_get_address_type(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *str;
+
+       if ((adapter->current_settings & MGMT_SETTING_LE) &&
+                               (adapter->bdaddr_type == BDADDR_LE_RANDOM))
+               str = "random";
+       else
+               str = "public";
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+       return TRUE;
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static gboolean property_get_le_address(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *user_data)
@@ -6581,39 +6741,97 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property,
        g_hash_table_foreach(uuids, iter_append_uuid, &entry);
        dbus_message_iter_close_container(iter, &entry);
 
-       g_hash_table_destroy(uuids);
+       g_hash_table_destroy(uuids);
+
+       return TRUE;
+}
+
+static gboolean property_exists_modalias(const GDBusPropertyTable *property,
+                                                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       return adapter->modalias ? TRUE : FALSE;
+}
+
+static gboolean property_get_modalias(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *str = adapter->modalias ? : "";
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+       return TRUE;
+}
+
+static int device_path_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *device = a;
+       const char *path = b;
+       const char *dev_path = device_get_path(device);
+
+       return strcasecmp(dev_path, path);
+}
+
+static DBusMessage *remove_device(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       const char *path;
+       GSList *list;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
+       if (!list)
+               return btd_error_does_not_exist(msg);
+
+       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+               return btd_error_not_ready(msg);
+
+       device = list->data;
+
+       btd_device_set_temporary(device, true);
+
+       if (!btd_device_is_connected(device)) {
+               btd_adapter_remove_device(adapter, device);
+               return dbus_message_new_method_return(msg);
+       }
 
-       return TRUE;
+       device_request_disconnect(device, msg);
+
+       return NULL;
 }
 
-static gboolean property_exists_modalias(const GDBusPropertyTable *property,
-                                                       void *user_data)
+static DBusMessage *get_discovery_filters(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
 {
-       struct btd_adapter *adapter = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter, array;
+       struct filter_parser *parser;
 
-       return adapter->modalias ? TRUE : FALSE;
-}
+       reply = dbus_message_new_method_return(msg);
 
-static gboolean property_get_modalias(const GDBusPropertyTable *property,
-                                       DBusMessageIter *iter, void *user_data)
-{
-       struct btd_adapter *adapter = user_data;
-       const char *str = adapter->modalias ? : "";
+       dbus_message_iter_init_append(reply, &iter);
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &array);
 
-       return TRUE;
-}
+       for (parser = parsers; parser && parser->name; parser++) {
+               dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+                                                       &parser->name);
+       }
 
-static int device_path_cmp(gconstpointer a, gconstpointer b)
-{
-       const struct btd_device *device = a;
-       const char *path = b;
-       const char *dev_path = device_get_path(device);
+       dbus_message_iter_close_container(&iter, &array);
 
-       return strcasecmp(dev_path, path);
+       return reply;
 }
 
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static DBusMessage *adapter_unpair_device(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
@@ -6855,45 +7073,12 @@ static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
 }
 #endif
 
-static DBusMessage *remove_device(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
-{
-       struct btd_adapter *adapter = user_data;
-       struct btd_device *device;
-       const char *path;
-       GSList *list;
-
-       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
-                                               DBUS_TYPE_INVALID) == FALSE)
-               return btd_error_invalid_args(msg);
-
-       list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
-       if (!list)
-               return btd_error_does_not_exist(msg);
-
-       if (!(adapter->current_settings & MGMT_SETTING_POWERED))
-               return btd_error_not_ready(msg);
-
-       device = list->data;
-
-       btd_device_set_temporary(device, true);
-
-       if (!btd_device_is_connected(device)) {
-               btd_adapter_remove_device(adapter, device);
-               return dbus_message_new_method_return(msg);
-       }
-
-       device_request_disconnect(device, msg);
-
-       return NULL;
-}
-
 static const GDBusMethodTable adapter_methods[] = {
-       { GDBUS_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
+       { GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
        { GDBUS_METHOD("SetDiscoveryFilter",
                                GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
                                set_discovery_filter) },
-       { GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
+       { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        { GDBUS_METHOD("StartCustomDiscovery",
                        GDBUS_ARGS({ "type", "s" }), NULL,
@@ -7019,6 +7204,9 @@ static const GDBusMethodTable adapter_methods[] = {
                        GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }),
                        le_read_host_suggested_default_data_length)},
 #endif
+       { GDBUS_METHOD("GetDiscoveryFilters", NULL,
+                       GDBUS_ARGS({ "filters", "as" }),
+                       get_discovery_filters) },
        { }
 };
 
@@ -7048,6 +7236,7 @@ static const GDBusSignalTable adapter_signals[] = {
 
 static const GDBusPropertyTable adapter_properties[] = {
        { "Address", "s", property_get_address },
+       { "AddressType", "s", property_get_address_type },
        { "Name", "s", property_get_name },
        { "Alias", "s", property_get_alias, property_set_alias },
        { "Class", "u", property_get_class },
@@ -7325,12 +7514,11 @@ static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
 {
        char filename[PATH_MAX];
        GKeyFile *key_file;
-       char address[18];
        char *str_irk;
        int ret;
 
-       ba2str(&adapter->bdaddr, address);
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity", address);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/identity",
+                                               adapter_dir(adapter));
 
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
@@ -7338,7 +7526,7 @@ static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
        str_irk = g_key_file_get_string(key_file, "General",
                                                "IdentityResolvingKey", NULL);
        if (!str_irk) {
-               info("No IRK for %s, creating new IRK", address);
+               info("No IRK stored");
                ret = generate_and_write_irk(irk, key_file, filename);
                g_key_file_free(key_file);
                return ret;
@@ -7805,7 +7993,6 @@ static void probe_devices(void *user_data)
 static void load_devices(struct btd_adapter *adapter)
 {
        char dirname[PATH_MAX];
-       char srcaddr[18];
        GSList *keys = NULL;
        GSList *ltks = NULL;
        GSList *irks = NULL;
@@ -7814,9 +8001,7 @@ static void load_devices(struct btd_adapter *adapter)
        DIR *dir;
        struct dirent *entry;
 
-       ba2str(&adapter->bdaddr, srcaddr);
-
-       snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", srcaddr);
+       snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", adapter_dir(adapter));
 
        dir = opendir(dirname);
        if (!dir) {
@@ -7860,8 +8045,8 @@ static void load_devices(struct btd_adapter *adapter)
                }
 }
 #endif
-               snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr,
-                               entry->d_name);
+               snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                                       adapter_dir(adapter), entry->d_name);
 
                key_file = g_key_file_new();
                g_key_file_load_from_file(key_file, filename, 0, NULL);
@@ -8619,6 +8804,16 @@ static void adapter_free(gpointer user_data)
 
        DBG("%p", adapter);
 
+       if (adapter->pairable_timeout_id > 0) {
+               g_source_remove(adapter->pairable_timeout_id);
+               adapter->pairable_timeout_id = 0;
+       }
+
+       if (adapter->passive_scan_timeout > 0) {
+               g_source_remove(adapter->passive_scan_timeout);
+               adapter->passive_scan_timeout = 0;
+       }
+
        if (adapter->load_ltks_timeout > 0)
                g_source_remove(adapter->load_ltks_timeout);
 
@@ -9507,17 +9702,16 @@ static void load_config(struct btd_adapter *adapter)
 {
        GKeyFile *key_file;
        char filename[PATH_MAX];
-       char address[18];
        struct stat st;
        GError *gerr = NULL;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        char *str;
 #endif
-       ba2str(&adapter->bdaddr, address);
 
        key_file = g_key_file_new();
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings",
+                                               adapter_dir(adapter));
 
        if (stat(filename, &st) < 0) {
                convert_config(adapter, filename, key_file);
@@ -9709,6 +9903,8 @@ static void adapter_remove(struct btd_adapter *adapter)
        g_slist_free(adapter->devices);
        adapter->devices = NULL;
 
+       discovery_cleanup(adapter);
+
        unload_drivers(adapter);
 
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -9927,6 +10123,17 @@ static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
        return got_match;
 }
 
+static void filter_duplicate_data(void *data, void *user_data)
+{
+       struct watch_client *client = data;
+       bool *duplicate = user_data;
+
+       if (*duplicate || !client->discovery_filter)
+               return;
+
+       *duplicate = client->discovery_filter->duplicate;
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void update_found_devices(struct btd_adapter *adapter,
                                        const bdaddr_t *bdaddr,
@@ -9953,6 +10160,7 @@ static void update_found_devices(struct btd_adapter *adapter,
        uint8_t allow_report;
 #endif
        char addr[18];
+       bool duplicate = false;
 
        memset(&eir_data, 0, sizeof(eir_data));
        eir_parse(&eir_data, data, data_len);
@@ -10040,8 +10248,8 @@ static void update_found_devices(struct btd_adapter *adapter,
                device_store_cached_name(dev, eir_data.name);
 
        /*
-        * If no client has requested discovery, then only update
-        * already paired devices (skip temporary ones).
+        * Only skip devices that are not connected, are temporary and there
+        * is no active discovery session ongoing.
         */
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (device_is_temporary(dev) && adapter->discovery_list == NULL &&
@@ -10054,7 +10262,8 @@ static void update_found_devices(struct btd_adapter *adapter,
        device_set_last_addr_type(dev, bdaddr_type);
        device_set_ipsp_connected(dev, FALSE, NULL);
 #else
-       if (device_is_temporary(dev) && !adapter->discovery_list) {
+       if (!btd_device_is_connected(dev) && (device_is_temporary(dev) &&
+                                                !adapter->discovery_list)) {
                eir_data_free(&eir_data);
                return;
        }
@@ -10111,6 +10320,9 @@ static void update_found_devices(struct btd_adapter *adapter,
 
        device_add_eir_uuids(dev, eir_data.services);
 
+       if (adapter->discovery_list)
+               g_slist_foreach(adapter->discovery_list, filter_duplicate_data,
+                                                               &duplicate);
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (eir_data.flags != 0)
                device_set_remote_feature_flag(dev, eir_data.flags);
@@ -10125,12 +10337,12 @@ static void update_found_devices(struct btd_adapter *adapter,
 #endif
 
        if (eir_data.msd_list) {
-               device_set_manufacturer_data(dev, eir_data.msd_list);
+               device_set_manufacturer_data(dev, eir_data.msd_list, duplicate);
                adapter_msd_notify(adapter, dev, eir_data.msd_list);
        }
 
        if (eir_data.sd_list)
-               device_set_service_data(dev, eir_data.sd_list);
+               device_set_service_data(dev, eir_data.sd_list, duplicate);
 
        if (bdaddr_type != BDADDR_BREDR)
                device_set_flags(dev, eir_data.flags);
@@ -10360,29 +10572,13 @@ static void adapter_stop(struct btd_adapter *adapter)
 
        cancel_passive_scanning(adapter);
 
-       while (adapter->set_filter_list) {
-               struct watch_client *client;
-
-               client = adapter->set_filter_list->data;
-
-               /* g_dbus_remove_watch will remove the client from the
-                * adapter's list and free it using the discovery_destroy
-                * function.
-                */
-               g_dbus_remove_watch(dbus_conn, client->watch);
-       }
-
-       while (adapter->discovery_list) {
-               struct watch_client *client;
+       g_slist_free_full(adapter->set_filter_list, discovery_free);
+       adapter->set_filter_list = NULL;
 
-               client = adapter->discovery_list->data;
+       g_slist_free_full(adapter->discovery_list, discovery_free);
+       adapter->discovery_list = NULL;
 
-               /* g_dbus_remove_watch will remove the client from the
-                * adapter's list and free it using the discovery_destroy
-                * function.
-                */
-               g_dbus_remove_watch(dbus_conn, client->watch);
-       }
+       discovery_cleanup(adapter);
 
        adapter->filtered_discovery = false;
        adapter->no_scan_restart_delay = false;
@@ -10575,8 +10771,9 @@ static void svc_complete(struct btd_device *dev, int err, void *user_data)
 }
 
 static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
-                                       const char *uuid, service_auth_cb cb,
-                                       void *user_data)
+                                       const char *uuid,
+                                       adapter_authorize_type check_for_connection,
+                                       service_auth_cb cb, void *user_data)
 {
        struct service_auth *auth;
        struct btd_device *device;
@@ -10592,7 +10789,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        }
 
        /* Device connected? */
-       if (!g_slist_find(adapter->connections, device))
+       if (check_for_connection && !g_slist_find(adapter->connections, device))
                btd_error(adapter->dev_id,
                        "Authorization request for non-connected device!?");
 
@@ -10606,7 +10803,12 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        auth->device = device;
        auth->adapter = adapter;
        auth->id = ++id;
-       auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+       if (check_for_connection)
+               auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
+       else {
+               if (adapter->auth_idle_id == 0)
+                       adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+       }
 
        g_queue_push_tail(adapter->auths, auth);
 
@@ -10625,7 +10827,8 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
                if (!adapter)
                        return 0;
 
-               return adapter_authorize(adapter, dst, uuid, cb, user_data);
+               return adapter_authorize(adapter, dst, uuid,
+                               ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
        }
 
        for (l = adapters; l != NULL; l = g_slist_next(l)) {
@@ -10633,7 +10836,8 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
 
                adapter = l->data;
 
-               id = adapter_authorize(adapter, dst, uuid, cb, user_data);
+               id = adapter_authorize(adapter, dst, uuid,
+                               ADAPTER_AUTHORIZE_CHECK_CONNECTED, cb, user_data);
                if (id != 0)
                        return id;
        }
@@ -10641,6 +10845,23 @@ guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
        return 0;
 }
 
+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+                                               const char *uuid, service_auth_cb cb,
+                                               void *user_data)
+{
+       struct btd_adapter *adapter;
+
+       if (bacmp(src, BDADDR_ANY) == 0)
+               return 0;
+
+       adapter = adapter_find(src);
+       if (!adapter)
+               return 0;
+
+       return adapter_authorize(adapter, dst, uuid,
+                       ADAPTER_AUTHORIZE_DISCONNECTED, cb, user_data);
+}
+
 static struct service_auth *find_authorization(guint id)
 {
        GSList *l;
@@ -11811,7 +12032,6 @@ static void store_link_key(struct btd_adapter *adapter,
                                struct btd_device *device, const uint8_t *key,
                                uint8_t type, uint8_t pin_length)
 {
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
@@ -11820,11 +12040,10 @@ static void store_link_key(struct btd_adapter *adapter,
        char *str;
        int i;
 
-       ba2str(btd_adapter_get_address(adapter), adapter_addr);
        ba2str(device_get_address(device), device_addr);
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
-                                                               device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                                       adapter_dir(adapter), device_addr);
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
@@ -11900,14 +12119,13 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
        bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
 }
 
-static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
-                               uint8_t bdaddr_type, const unsigned char *key,
-                               uint8_t master, uint8_t authenticated,
-                               uint8_t enc_size, uint16_t ediv,
-                               uint64_t rand)
+static void store_longtermkey(struct btd_adapter *adapter, const bdaddr_t *peer,
+                                uint8_t bdaddr_type, const unsigned char *key,
+                                uint8_t master, uint8_t authenticated,
+                                uint8_t enc_size, uint16_t ediv,
+                                uint64_t rand)
 {
        const char *group = master ? "LongTermKey" : "SlaveLongTermKey";
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
@@ -11921,11 +12139,10 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
                return;
        }
 
-       ba2str(local, adapter_addr);
        ba2str(peer, device_addr);
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
-                                                               device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                       adapter_dir(adapter), device_addr);
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
@@ -11938,7 +12155,7 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
        g_key_file_set_string(key_file, group, "Key", key_str);
 
        g_key_file_set_integer(key_file, group, "Authenticated",
-                                                       authenticated);
+                       authenticated);
        g_key_file_set_integer(key_file, group, "EncSize", enc_size);
 
        g_key_file_set_integer(key_file, group, "EDiv", ediv);
@@ -12002,7 +12219,6 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
 
        if (persistent) {
                const struct mgmt_ltk_info *key = &ev->key;
-               const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
                uint16_t ediv;
                uint64_t rand;
 
@@ -12011,11 +12227,11 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                device_get_tizen_addr(device, addr->type, &t_addr);
-               store_longtermkey(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type,
+               store_longtermkey(adapter, &t_addr.bdaddr, t_addr.bdaddr_type,
                                        key->val, key->master, key->type,
                                        key->enc_size, ediv, rand);
 #else
-               store_longtermkey(bdaddr, &key->addr.bdaddr,
+               store_longtermkey(adapter, &key->addr.bdaddr,
                                        key->addr.type, key->val, key->master,
                                        key->type, key->enc_size, ediv, rand);
 #endif
@@ -12026,12 +12242,11 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
        bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
 }
 
-static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer,
-                               uint8_t bdaddr_type, const unsigned char *key,
-                               uint32_t counter, uint8_t type)
+static void store_csrk(struct btd_adapter *adapter, const bdaddr_t *peer,
+                                uint8_t bdaddr_type, const unsigned char *key,
+                                uint32_t counter, uint8_t type)
 {
        const char *group;
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
@@ -12063,11 +12278,10 @@ static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer,
                return;
        }
 
-       ba2str(local, adapter_addr);
        ba2str(peer, device_addr);
 
-       snprintf(filename, sizeof(filename), STORAGEDIR "/%s/%s/info",
-                                               adapter_addr, device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                       adapter_dir(adapter), device_addr);
 
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
@@ -12095,7 +12309,6 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
        const struct mgmt_addr_info *addr = &ev->key.addr;
        const struct mgmt_csrk_info *key = &ev->key;
        struct btd_adapter *adapter = user_data;
-       const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
        struct btd_device *device;
        char dst[18];
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -12124,10 +12337,10 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        device_get_tizen_addr(device, addr->type, &t_addr);
-       store_csrk(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
+       store_csrk(adapter, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
                                                                key->type);
 #else
-       store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
+       store_csrk(adapter, &key->addr.bdaddr, key->addr.type, key->val, 0,
                                                                key->type);
 #endif
 
@@ -12137,7 +12350,6 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
 static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
                                uint8_t bdaddr_type, const unsigned char *key)
 {
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
@@ -12146,11 +12358,10 @@ static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
        size_t length = 0;
        int i;
 
-       ba2str(&adapter->bdaddr, adapter_addr);
        ba2str(peer, device_addr);
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
-                                                               device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                                       adapter_dir(adapter), device_addr);
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
@@ -12196,8 +12407,10 @@ static void new_irk_callback(uint16_t index, uint16_t length,
                device = btd_adapter_get_device(adapter, &ev->rpa,
                                BDADDR_LE_RANDOM);
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+               device = btd_adapter_get_device(adapter, &ev->rpa,
+                                                       BDADDR_LE_RANDOM);
                duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
-                               addr->type);
+                                                               addr->type);
                if (duplicate == device)
                        duplicate = NULL;
 #else
@@ -12244,20 +12457,18 @@ static void store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer,
                                uint16_t max_interval, uint16_t latency,
                                uint16_t timeout)
 {
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
        char *store_data;
        size_t length = 0;
 
-       ba2str(&adapter->bdaddr, adapter_addr);
        ba2str(peer, device_addr);
 
        DBG("");
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
-                                                               device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                                       adapter_dir(adapter), device_addr);
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
@@ -12657,15 +12868,12 @@ static int adapter_register(struct btd_adapter *adapter)
                return -EINVAL;
        }
 
-       if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL) {
-               /* Don't start advertising managers on non-LE controllers. */
-               if (adapter->supported_settings & MGMT_SETTING_LE) {
-                       adapter->adv_manager = btd_adv_manager_new(adapter);
-               } else {
-                       btd_info(adapter->dev_id,
-                               "LEAdvertisingManager skipped, LE unavailable");
-               }
-       }
+       /* Don't start advertising managers on non-LE controllers. */
+       if (adapter->supported_settings & MGMT_SETTING_LE)
+               adapter->adv_manager = btd_adv_manager_new(adapter);
+       else
+               btd_info(adapter->dev_id,
+                       "LEAdvertisingManager skipped, LE unavailable");
 
        db = btd_gatt_database_get_db(adapter->database);
        adapter->db_id = gatt_db_register(db, services_modified,
@@ -12942,14 +13150,12 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
 static void remove_keys(struct btd_adapter *adapter,
                                        struct btd_device *device, uint8_t type)
 {
-       char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX];
        GKeyFile *key_file;
        gsize length = 0;
        char *str;
 
-       ba2str(btd_adapter_get_address(adapter), adapter_addr);
        ba2str(device_get_address(device), device_addr);
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -12957,8 +13163,8 @@ static void remove_keys(struct btd_adapter *adapter,
                ba2str(device_get_rpa(device), device_addr);
 #endif
 
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
-                                                               device_addr);
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info",
+                                       adapter_dir(adapter), device_addr);
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
@@ -13007,6 +13213,143 @@ static void unpaired_callback(uint16_t index, uint16_t length,
        device_set_unpaired(device, ev->addr.type);
 }
 
+static void clear_devices_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("Failed to clear devices: %s (0x%02x)",
+                                               mgmt_errstr(status), status);
+               return;
+       }
+}
+
+static int clear_devices(struct btd_adapter *adapter)
+{
+       struct mgmt_cp_remove_device cp;
+
+       if (!kernel_conn_control)
+               return 0;
+
+       memset(&cp, 0, sizeof(cp));
+
+       DBG("sending clear devices command for index %u", adapter->dev_id);
+
+       if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               clear_devices_complete, adapter, NULL) > 0)
+               return 0;
+
+       btd_error(adapter->dev_id, "Failed to clear devices for index %u",
+                                                       adapter->dev_id);
+
+       return -EIO;
+}
+
+static bool get_static_addr(struct btd_adapter *adapter)
+{
+       struct bt_crypto *crypto;
+       GKeyFile *file;
+       char **addrs;
+       char mfg[7];
+       char *str;
+       bool ret;
+       gsize len, i;
+
+       snprintf(mfg, sizeof(mfg), "0x%04x", adapter->manufacturer);
+
+       file = g_key_file_new();
+       g_key_file_load_from_file(file, STORAGEDIR "/addresses", 0, NULL);
+       addrs = g_key_file_get_string_list(file, "Static", mfg, &len, NULL);
+       if (addrs) {
+               for (i = 0; i < len; i++) {
+                       bdaddr_t addr;
+
+                       str2ba(addrs[i], &addr);
+                       if (adapter_find(&addr))
+                               continue;
+
+                       /* Usable address found in list */
+                       bacpy(&adapter->bdaddr, &addr);
+                       adapter->bdaddr_type = BDADDR_LE_RANDOM;
+                       ret = true;
+                       goto done;
+               }
+
+               len++;
+               addrs = g_renew(char *, addrs, len + 1);
+       } else {
+               len = 1;
+               addrs = g_new(char *, len + 1);
+       }
+
+       /* Initialize slot for new address */
+       addrs[len - 1] = g_malloc(18);
+       addrs[len] = NULL;
+
+       crypto = bt_crypto_new();
+       if (!crypto) {
+               error("Failed to open crypto");
+               ret = false;
+               goto done;
+       }
+
+       ret = bt_crypto_random_bytes(crypto, &adapter->bdaddr,
+                                               sizeof(adapter->bdaddr));
+       if (!ret) {
+               error("Failed to generate static address");
+               bt_crypto_unref(crypto);
+               goto done;
+       }
+
+       bt_crypto_unref(crypto);
+
+       adapter->bdaddr.b[5] |= 0xc0;
+       adapter->bdaddr_type = BDADDR_LE_RANDOM;
+
+       ba2str(&adapter->bdaddr, addrs[len - 1]);
+
+       g_key_file_set_string_list(file, "Static", mfg,
+                                               (const char **)addrs, len);
+
+       str = g_key_file_to_data(file, &len, NULL);
+       g_file_set_contents(STORAGEDIR "/addresses", str, len, NULL);
+       g_free(str);
+
+       ret = true;
+
+done:
+       g_key_file_free(file);
+       g_strfreev(addrs);
+
+       return ret;
+}
+
+static bool set_static_addr(struct btd_adapter *adapter)
+{
+       struct mgmt_cp_set_static_address cp;
+
+       /* dual-mode adapters must have a public address */
+       if (adapter->supported_settings & MGMT_SETTING_BREDR)
+               return false;
+
+       if (!(adapter->supported_settings & MGMT_SETTING_LE))
+               return false;
+
+       DBG("Setting static address");
+
+       if (!get_static_addr(adapter))
+               return false;
+
+       bacpy(&cp.bdaddr, &adapter->bdaddr);
+       if (mgmt_send(adapter->mgmt, MGMT_OP_SET_STATIC_ADDRESS,
+                               adapter->dev_id, sizeof(cp), &cp,
+                               NULL, NULL, NULL) > 0) {
+               return true;
+       }
+
+       return false;
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static uint8_t *generate_irk(void)
 {
@@ -13265,38 +13608,6 @@ int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
 }
 #endif
 
-static void clear_devices_complete(uint8_t status, uint16_t length,
-                                       const void *param, void *user_data)
-{
-       if (status != MGMT_STATUS_SUCCESS) {
-               error("Failed to clear devices: %s (0x%02x)",
-                                               mgmt_errstr(status), status);
-               return;
-       }
-}
-
-static int clear_devices(struct btd_adapter *adapter)
-{
-       struct mgmt_cp_remove_device cp;
-
-       if (!kernel_conn_control)
-               return 0;
-
-       memset(&cp, 0, sizeof(cp));
-
-       DBG("sending clear devices command for index %u", adapter->dev_id);
-
-       if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEVICE,
-                               adapter->dev_id, sizeof(cp), &cp,
-                               clear_devices_complete, adapter, NULL) > 0)
-               return 0;
-
-       btd_error(adapter->dev_id, "Failed to clear devices for index %u",
-                                                       adapter->dev_id);
-
-       return -EIO;
-}
-
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static gboolean adapter_start_idle_cb(gpointer user_data)
 {
@@ -13331,26 +13642,21 @@ static void read_info_complete(uint8_t status, uint16_t length,
                goto failed;
        }
 
-       if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) {
-               btd_error(adapter->dev_id, "No Bluetooth address for index %u",
-                                                       adapter->dev_id);
-               goto failed;
-       }
-
        /*
-        * Store controller information for device address, class of device,
-        * device name, short name and settings.
+        * Store controller information for class of device, device
+        * name, short name and settings.
         *
         * During the lifetime of the controller these will be updated by
         * events and the information is required to keep the current
         * state of the controller.
         */
-       bacpy(&adapter->bdaddr, &rp->bdaddr);
        adapter->dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
                                                (rp->dev_class[2] << 16);
        adapter->name = g_strdup((const char *) rp->name);
        adapter->short_name = g_strdup((const char *) rp->short_name);
 
+       adapter->manufacturer = btohs(rp->manufacturer);
+
        adapter->supported_settings = btohl(rp->supported_settings);
        adapter->current_settings = btohl(rp->current_settings);
 
@@ -13361,6 +13667,21 @@ static void read_info_complete(uint8_t status, uint16_t length,
        clear_uuids(adapter);
        clear_devices(adapter);
 
+       if (bacmp(&rp->bdaddr, BDADDR_ANY) == 0) {
+               if (!set_static_addr(adapter)) {
+                       btd_error(adapter->dev_id,
+                                       "No Bluetooth address for index %u",
+                                       adapter->dev_id);
+                       goto failed;
+               }
+       } else {
+               bacpy(&adapter->bdaddr, &rp->bdaddr);
+               if (!(adapter->supported_settings & MGMT_SETTING_LE))
+                       adapter->bdaddr_type = BDADDR_BREDR;
+               else
+                       adapter->bdaddr_type = BDADDR_LE_PUBLIC;
+       }
+
        missing_settings = adapter->current_settings ^
                                                adapter->supported_settings;
 
index d900d9d..4dd060c 100644 (file)
@@ -192,6 +192,8 @@ int btd_register_adapter_driver(struct btd_adapter_driver *driver);
 void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
 guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
                const char *uuid, service_auth_cb cb, void *user_data);
+guint btd_request_authorization_cable_configured(const bdaddr_t *src, const bdaddr_t *dst,
+               const char *uuid, service_auth_cb cb, void *user_data);
 int btd_cancel_authorization(guint id);
 
 int btd_adapter_restore_powered(struct btd_adapter *adapter);
index de07a98..94a8c40 100755 (executable)
@@ -21,6 +21,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <errno.h>
 
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
@@ -33,6 +34,7 @@
 #include "dbus-common.h"
 #include "error.h"
 #include "log.h"
+#include "eir.h"
 #include "src/shared/ad.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/queue.h"
@@ -47,7 +49,9 @@ struct btd_adv_manager {
        struct mgmt *mgmt;
        uint16_t mgmt_index;
        uint8_t max_adv_len;
+       uint8_t max_scan_rsp_len;
        uint8_t max_ads;
+       uint32_t supported_flags;
        unsigned int instance_bitmap;
 };
 
@@ -58,12 +62,18 @@ struct btd_adv_client {
        struct btd_adv_manager *manager;
        char *owner;
        char *path;
+       char *name;
+       uint16_t appearance;
+       uint16_t duration;
+       uint16_t timeout;
+       unsigned int to_id;
        GDBusClient *client;
        GDBusProxy *proxy;
        DBusMessage *reg;
        uint8_t type; /* Advertising type */
-       bool include_tx_power;
+       uint32_t flags;
        struct bt_ad *data;
+       struct bt_ad *scan;
        uint8_t instance;
 };
 
@@ -90,6 +100,9 @@ static void client_free(void *data)
 {
        struct btd_adv_client *client = data;
 
+       if (client->to_id > 0)
+               g_source_remove(client->to_id);
+
        if (client->client) {
                g_dbus_client_set_disconnect_watch(client->client, NULL, NULL);
                g_dbus_client_unref(client->client);
@@ -100,6 +113,7 @@ static void client_free(void *data)
                                                client->instance);
 
        bt_ad_unref(client->data);
+       bt_ad_unref(client->scan);
 
        g_dbus_proxy_unref(client->proxy);
 
@@ -109,6 +123,7 @@ static void client_free(void *data)
        if (client->path)
                g_free(client->path);
 
+       free(client->name);
        free(client);
 }
 
@@ -122,20 +137,11 @@ static gboolean client_free_idle_cb(void *data)
 static void client_release(void *data)
 {
        struct btd_adv_client *client = data;
-       DBusMessage *message;
 
        DBG("Releasing advertisement %s, %s", client->owner, client->path);
 
-       message = dbus_message_new_method_call(client->owner, client->path,
-                                                       LE_ADVERTISEMENT_IFACE,
-                                                       "Release");
-
-       if (!message) {
-               error("Couldn't allocate D-Bus message");
-               return;
-       }
-
-       g_dbus_send_message(btd_get_dbus_connection(), message);
+       g_dbus_proxy_method_call(client->proxy, "Release", NULL, NULL, NULL,
+                                                                       NULL);
 }
 
 static void client_destroy(void *data)
@@ -144,6 +150,22 @@ static void client_destroy(void *data)
        client_free(data);
 }
 
+static void remove_advertising(struct btd_adv_manager *manager,
+                                               uint8_t instance)
+{
+       struct mgmt_cp_remove_advertising cp;
+
+       if (instance)
+               DBG("instance %u", instance);
+       else
+               DBG("all instances");
+
+       cp.instance = instance;
+
+       mgmt_send(manager->mgmt, MGMT_OP_REMOVE_ADVERTISING,
+                       manager->mgmt_index, sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
 static void client_remove(void *data)
 {
        struct btd_adv_client *client = data;
@@ -160,6 +182,14 @@ static void client_remove(void *data)
        queue_remove(client->manager->clients, client);
 
        g_idle_add(client_free_idle_cb, client);
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                               adapter_get_path(client->manager->adapter),
+                               LE_ADVERTISING_MGR_IFACE, "SupportedInstances");
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                               adapter_get_path(client->manager->adapter),
+                               LE_ADVERTISING_MGR_IFACE, "ActiveInstances");
 }
 
 static void client_disconnect_cb(DBusConnection *conn, void *user_data)
@@ -169,45 +199,39 @@ static void client_disconnect_cb(DBusConnection *conn, void *user_data)
        client_remove(user_data);
 }
 
-static bool parse_type(GDBusProxy *proxy, uint8_t *type)
+static bool parse_type(DBusMessageIter *iter, struct btd_adv_client *client)
 {
-       DBusMessageIter iter;
        const char *msg_type;
 
-       if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
                return false;
 
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
-               return false;
-
-       dbus_message_iter_get_basic(&iter, &msg_type);
+       dbus_message_iter_get_basic(iter, &msg_type);
 
        if (!g_strcmp0(msg_type, "broadcast")) {
-               *type = AD_TYPE_BROADCAST;
+               client->type = AD_TYPE_BROADCAST;
                return true;
        }
 
        if (!g_strcmp0(msg_type, "peripheral")) {
-               *type = AD_TYPE_PERIPHERAL;
+               client->type = AD_TYPE_PERIPHERAL;
                return true;
        }
 
        return false;
 }
 
-static bool parse_service_uuids(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_service_uuids(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessageIter iter, ariter;
-
-       if (!g_dbus_proxy_get_property(proxy, "ServiceUUIDs", &iter))
-               return true;
+       DBusMessageIter ariter;
 
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
                return false;
 
-       dbus_message_iter_recurse(&iter, &ariter);
+       dbus_message_iter_recurse(iter, &ariter);
 
-       bt_ad_clear_service_uuid(data);
+       bt_ad_clear_service_uuid(client->data);
 
        while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
                const char *uuid_str;
@@ -220,7 +244,7 @@ static bool parse_service_uuids(GDBusProxy *proxy, struct bt_ad *data)
                if (bt_string_to_uuid(&uuid, uuid_str) < 0)
                        goto fail;
 
-               if (!bt_ad_add_service_uuid(data, &uuid))
+               if (!bt_ad_add_service_uuid(client->data, &uuid))
                        goto fail;
 
                dbus_message_iter_next(&ariter);
@@ -229,23 +253,21 @@ static bool parse_service_uuids(GDBusProxy *proxy, struct bt_ad *data)
        return true;
 
 fail:
-       bt_ad_clear_service_uuid(data);
+       bt_ad_clear_service_uuid(client->data);
        return false;
 }
 
-static bool parse_solicit_uuids(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_solicit_uuids(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessageIter iter, ariter;
+       DBusMessageIter ariter;
 
-       if (!g_dbus_proxy_get_property(proxy, "SolicitUUIDs", &iter))
-               return true;
-
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
                return false;
 
-       dbus_message_iter_recurse(&iter, &ariter);
+       dbus_message_iter_recurse(iter, &ariter);
 
-       bt_ad_clear_solicit_uuid(data);
+       bt_ad_clear_solicit_uuid(client->data);
 
        while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
                const char *uuid_str;
@@ -258,7 +280,7 @@ static bool parse_solicit_uuids(GDBusProxy *proxy, struct bt_ad *data)
                if (bt_string_to_uuid(&uuid, uuid_str) < 0)
                        goto fail;
 
-               if (!bt_ad_add_solicit_uuid(data, &uuid))
+               if (!bt_ad_add_solicit_uuid(client->data, &uuid))
                        goto fail;
 
                dbus_message_iter_next(&ariter);
@@ -267,23 +289,21 @@ static bool parse_solicit_uuids(GDBusProxy *proxy, struct bt_ad *data)
        return true;
 
 fail:
-       bt_ad_clear_solicit_uuid(data);
+       bt_ad_clear_solicit_uuid(client->data);
        return false;
 }
 
-static bool parse_manufacturer_data(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_manufacturer_data(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessageIter iter, entries;
-
-       if (!g_dbus_proxy_get_property(proxy, "ManufacturerData", &iter))
-               return true;
+       DBusMessageIter entries;
 
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
                return false;
 
-       dbus_message_iter_recurse(&iter, &entries);
+       dbus_message_iter_recurse(iter, &entries);
 
-       bt_ad_clear_manufacturer_data(data);
+       bt_ad_clear_manufacturer_data(client->data);
 
        while (dbus_message_iter_get_arg_type(&entries)
                                                == DBUS_TYPE_DICT_ENTRY) {
@@ -314,8 +334,8 @@ static bool parse_manufacturer_data(GDBusProxy *proxy, struct bt_ad *data)
 
                DBG("Adding ManufacturerData for %04x", manuf_id);
 
-               if (!bt_ad_add_manufacturer_data(data, manuf_id, manuf_data,
-                                                                       len))
+               if (!bt_ad_add_manufacturer_data(client->data, manuf_id,
+                                                       manuf_data, len))
                        goto fail;
 
                dbus_message_iter_next(&entries);
@@ -324,23 +344,21 @@ static bool parse_manufacturer_data(GDBusProxy *proxy, struct bt_ad *data)
        return true;
 
 fail:
-       bt_ad_clear_manufacturer_data(data);
+       bt_ad_clear_manufacturer_data(client->data);
        return false;
 }
 
-static bool parse_service_data(GDBusProxy *proxy, struct bt_ad *data)
+static bool parse_service_data(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessageIter iter, entries;
-
-       if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
-               return true;
+       DBusMessageIter entries;
 
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
                return false;
 
-       dbus_message_iter_recurse(&iter, &entries);
+       dbus_message_iter_recurse(iter, &entries);
 
-       bt_ad_clear_service_data(data);
+       bt_ad_clear_service_data(client->data);
 
        while (dbus_message_iter_get_arg_type(&entries)
                                                == DBUS_TYPE_DICT_ENTRY) {
@@ -375,7 +393,8 @@ static bool parse_service_data(GDBusProxy *proxy, struct bt_ad *data)
 
                DBG("Adding ServiceData for %s", uuid_str);
 
-               if (!bt_ad_add_service_data(data, &uuid, service_data, len))
+               if (!bt_ad_add_service_data(client->data, &uuid, service_data,
+                                                                       len))
                        goto fail;
 
                dbus_message_iter_next(&entries);
@@ -384,72 +403,150 @@ static bool parse_service_data(GDBusProxy *proxy, struct bt_ad *data)
        return true;
 
 fail:
-       bt_ad_clear_service_data(data);
+       bt_ad_clear_service_data(client->data);
        return false;
 }
 
-static bool parse_include_tx_power(GDBusProxy *proxy, bool *included)
+static struct adv_include {
+       uint8_t flag;
+       const char *name;
+} includes[] = {
+       { MGMT_ADV_FLAG_TX_POWER, "tx-power" },
+       { MGMT_ADV_FLAG_APPEARANCE, "appearance" },
+       { MGMT_ADV_FLAG_LOCAL_NAME, "local-name" },
+       { },
+};
+
+static bool parse_includes(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
+{
+       DBusMessageIter entries;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               return false;
+
+       dbus_message_iter_recurse(iter, &entries);
+
+       while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_STRING) {
+               const char *str;
+               struct adv_include *inc;
+
+               dbus_message_iter_get_basic(&entries, &str);
+
+               for (inc = includes; inc && inc->name; inc++) {
+                       if (strcmp(str, inc->name))
+                               continue;
+
+                       if (!(client->manager->supported_flags & inc->flag))
+                               continue;
+
+                       DBG("Including Feature: %s", str);
+
+                       client->flags |= inc->flag;
+               }
+
+               dbus_message_iter_next(&entries);
+       }
+
+       return true;
+}
+
+static bool parse_local_name(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessageIter iter;
-       dbus_bool_t b;
+       const char *name;
 
-       if (!g_dbus_proxy_get_property(proxy, "IncludeTxPower", &iter))
-               return true;
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+               return false;
 
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+       if (client->flags & MGMT_ADV_FLAG_LOCAL_NAME) {
+               error("Local name already included");
                return false;
+       }
 
-       dbus_message_iter_get_basic(&iter, &b);
+       dbus_message_iter_get_basic(iter, &name);
 
-       *included = b;
+       free(client->name);
+       client->name = strdup(name);
 
        return true;
 }
 
-static void add_client_complete(struct btd_adv_client *client, uint8_t status)
+static bool parse_appearance(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
 {
-       DBusMessage *reply;
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+               return false;
 
-       if (status) {
-               error("Failed to add advertisement: %s (0x%02x)",
-                                               mgmt_errstr(status), status);
-               reply = btd_error_failed(client->reg,
-                                       "Failed to register advertisement");
-               queue_remove(client->manager->clients, client);
-               g_idle_add(client_free_idle_cb, client);
+       if (client->flags & MGMT_ADV_FLAG_APPEARANCE) {
+               error("Appearance already included");
+               return false;
+       }
 
-       } else
-               reply = dbus_message_new_method_return(client->reg);
+       dbus_message_iter_get_basic(iter, &client->appearance);
 
-       g_dbus_send_message(btd_get_dbus_connection(), reply);
-       dbus_message_unref(client->reg);
-       client->reg = NULL;
+       return true;
 }
 
-static void add_adv_callback(uint8_t status, uint16_t length,
-                                         const void *param, void *user_data)
+static bool parse_duration(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
+{
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+               return false;
+
+       dbus_message_iter_get_basic(iter, &client->duration);
+
+       return true;
+}
+
+static gboolean client_timeout(void *user_data)
 {
        struct btd_adv_client *client = user_data;
-       const struct mgmt_rp_add_advertising *rp = param;
 
-       if (status)
-               goto done;
+       DBG("");
 
-       if (!param || length < sizeof(*rp)) {
-               status = MGMT_STATUS_FAILED;
-               goto done;
-       }
+       client->to_id = 0;
 
-       client->instance = rp->instance;
+       client_release(client);
+       client_remove(client);
 
-       g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb,
-                                                                       client);
-       DBG("Advertisement registered: %s", client->path);
+       return FALSE;
+}
 
-done:
-       add_client_complete(client, status);
+static bool parse_timeout(DBusMessageIter *iter,
+                                       struct btd_adv_client *client)
+{
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
+               return false;
+
+       dbus_message_iter_get_basic(iter, &client->timeout);
+
+       if (client->to_id)
+               g_source_remove(client->to_id);
+
+       client->to_id = g_timeout_add_seconds(client->timeout, client_timeout,
+                                                               client);
+
+       return true;
 }
 
+static struct adv_parser {
+       const char *name;
+       bool (*func)(DBusMessageIter *iter, struct btd_adv_client *client);
+} parsers[] = {
+       { "Type", parse_type },
+       { "ServiceUUIDs", parse_service_uuids },
+       { "SolicitUUIDs", parse_solicit_uuids },
+       { "ManufacturerData", parse_manufacturer_data },
+       { "ServiceData", parse_service_data },
+       { "Includes", parse_includes },
+       { "LocalName", parse_local_name },
+       { "Appearance", parse_appearance },
+       { "Duration", parse_duration },
+       { "Timeout", parse_timeout },
+       { },
+};
+
 static size_t calc_max_adv_len(struct btd_adv_client *client, uint32_t flags)
 {
        size_t max = client->manager->max_adv_len;
@@ -471,12 +568,56 @@ static size_t calc_max_adv_len(struct btd_adv_client *client, uint32_t flags)
        return max;
 }
 
-static DBusMessage *refresh_advertisement(struct btd_adv_client *client)
+static uint8_t *generate_adv_data(struct btd_adv_client *client,
+                                               uint32_t *flags, size_t *len)
+{
+       if ((*flags & MGMT_ADV_FLAG_APPEARANCE) ||
+                                       client->appearance != UINT16_MAX) {
+               uint16_t appearance;
+
+               appearance = client->appearance;
+               if (appearance == UINT16_MAX)
+                       /* TODO: Get the appearance from the adaptor once
+                        * supported.
+                        */
+                       appearance = 0x000;
+
+               bt_ad_add_appearance(client->data, appearance);
+       }
+
+       return bt_ad_generate(client->data, len);
+}
+
+static uint8_t *generate_scan_rsp(struct btd_adv_client *client,
+                                               uint32_t *flags, size_t *len)
+{
+       struct btd_adv_manager *manager = client->manager;
+       const char *name;
+
+       if (!(*flags & MGMT_ADV_FLAG_LOCAL_NAME) && !client->name) {
+               *len = 0;
+               return NULL;
+       }
+
+       *flags &= ~MGMT_ADV_FLAG_LOCAL_NAME;
+
+       name = client->name;
+       if (!name)
+               name = btd_adapter_get_name(manager->adapter);
+
+       bt_ad_add_name(client->scan, name);
+
+       return bt_ad_generate(client->scan, len);
+}
+
+static int refresh_adv(struct btd_adv_client *client, mgmt_request_func_t func)
 {
        struct mgmt_cp_add_advertising *cp;
        uint8_t param_len;
        uint8_t *adv_data;
        size_t adv_data_len;
+       uint8_t *scan_rsp;
+       size_t scan_rsp_len = -1;
        uint32_t flags = 0;
 
        DBG("Refreshing advertisement: %s", client->path);
@@ -484,86 +625,148 @@ static DBusMessage *refresh_advertisement(struct btd_adv_client *client)
        if (client->type == AD_TYPE_PERIPHERAL)
                flags = MGMT_ADV_FLAG_CONNECTABLE | MGMT_ADV_FLAG_DISCOV;
 
-       if (client->include_tx_power)
-               flags |= MGMT_ADV_FLAG_TX_POWER;
-
-       adv_data = bt_ad_generate(client->data, &adv_data_len);
+       flags |= client->flags;
 
+       adv_data = generate_adv_data(client, &flags, &adv_data_len);
        if (!adv_data || (adv_data_len > calc_max_adv_len(client, flags))) {
                error("Advertising data too long or couldn't be generated.");
+               return -EINVAL;
+       }
 
-               return g_dbus_create_error(client->reg, ERROR_INTERFACE
-                                               ".InvalidLength",
-                                               "Advertising data too long.");
+       scan_rsp = generate_scan_rsp(client, &flags, &scan_rsp_len);
+       if (!scan_rsp && scan_rsp_len) {
+               error("Scan data couldn't be generated.");
+               return -EINVAL;
        }
 
-       param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len;
+       param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len +
+                                                       scan_rsp_len;
 
        cp = malloc0(param_len);
-
        if (!cp) {
                error("Couldn't allocate for MGMT!");
-
                free(adv_data);
-
-               return btd_error_failed(client->reg, "Failed");
+               free(scan_rsp);
+               return -ENOMEM;
        }
 
        cp->flags = htobl(flags);
        cp->instance = client->instance;
+       cp->duration = client->duration;
        cp->adv_data_len = adv_data_len;
+       cp->scan_rsp_len = scan_rsp_len;
        memcpy(cp->data, adv_data, adv_data_len);
+       memcpy(cp->data + adv_data_len, scan_rsp, scan_rsp_len);
 
        free(adv_data);
+       free(scan_rsp);
 
        if (!mgmt_send(client->manager->mgmt, MGMT_OP_ADD_ADVERTISING,
                                client->manager->mgmt_index, param_len, cp,
-                               add_adv_callback, client, NULL)) {
+                               func, client, NULL)) {
                error("Failed to add Advertising Data");
-
                free(cp);
-
-               return btd_error_failed(client->reg, "Failed");
+               return -EINVAL;
        }
 
        free(cp);
 
-       return NULL;
+       return 0;
 }
 
-static DBusMessage *parse_advertisement(struct btd_adv_client *client)
+static void properties_changed(GDBusProxy *proxy, const char *name,
+                                       DBusMessageIter *iter, void *user_data)
 {
-       if (!parse_type(client->proxy, &client->type)) {
-               error("Failed to read \"Type\" property of advertisement");
-               goto fail;
-       }
+       struct btd_adv_client *client = user_data;
+       struct adv_parser *parser;
 
-       if (!parse_service_uuids(client->proxy, client->data)) {
-               error("Property \"ServiceUUIDs\" failed to parse");
-               goto fail;
-       }
+       for (parser = parsers; parser && parser->name; parser++) {
+               if (strcmp(parser->name, name))
+                       continue;
 
-       if (!parse_solicit_uuids(client->proxy, client->data)) {
-               error("Property \"SolicitUUIDs\" failed to parse");
-               goto fail;
+               if (parser->func(iter, client)) {
+                       refresh_adv(client, NULL);
+                       break;
+               }
        }
+}
 
-       if (!parse_manufacturer_data(client->proxy, client->data)) {
-               error("Property \"ManufacturerData\" failed to parse");
-               goto fail;
-       }
+static void add_client_complete(struct btd_adv_client *client, uint8_t status)
+{
+       DBusMessage *reply;
 
-       if (!parse_service_data(client->proxy, client->data)) {
-               error("Property \"ServiceData\" failed to parse");
-               goto fail;
+       if (status) {
+               error("Failed to add advertisement: %s (0x%02x)",
+                                               mgmt_errstr(status), status);
+               reply = btd_error_failed(client->reg,
+                                       "Failed to register advertisement");
+               queue_remove(client->manager->clients, client);
+               g_idle_add(client_free_idle_cb, client);
+
+       } else
+               reply = dbus_message_new_method_return(client->reg);
+
+       g_dbus_send_message(btd_get_dbus_connection(), reply);
+       dbus_message_unref(client->reg);
+       client->reg = NULL;
+}
+
+static void add_adv_callback(uint8_t status, uint16_t length,
+                                         const void *param, void *user_data)
+{
+       struct btd_adv_client *client = user_data;
+       const struct mgmt_rp_add_advertising *rp = param;
+
+       if (status)
+               goto done;
+
+       if (!param || length < sizeof(*rp)) {
+               status = MGMT_STATUS_FAILED;
+               goto done;
        }
 
-       if (!parse_include_tx_power(client->proxy, &client->include_tx_power)) {
-               error("Property \"IncludeTxPower\" failed to parse");
-               goto fail;
+       client->instance = rp->instance;
+
+       g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb,
+                                                                       client);
+       DBG("Advertisement registered: %s", client->path);
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                               adapter_get_path(client->manager->adapter),
+                               LE_ADVERTISING_MGR_IFACE, "SupportedInstances");
+
+       g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                               adapter_get_path(client->manager->adapter),
+                               LE_ADVERTISING_MGR_IFACE, "ActiveInstances");
+
+       g_dbus_proxy_set_property_watch(client->proxy, properties_changed,
+                                                               client);
+
+done:
+       add_client_complete(client, status);
+}
+
+static DBusMessage *parse_advertisement(struct btd_adv_client *client)
+{
+       struct adv_parser *parser;
+       int err;
+
+       for (parser = parsers; parser && parser->name; parser++) {
+               DBusMessageIter iter;
+
+               if (!g_dbus_proxy_get_property(client->proxy, parser->name,
+                                                               &iter))
+                       continue;
+
+               if (!parser->func(&iter, client)) {
+                       error("Error parsing %s property", parser->name);
+                       goto fail;
+               }
        }
 
-       return refresh_advertisement(client);
+       err = refresh_adv(client, add_adv_callback);
+       if (!err)
+               return NULL;
 
 fail:
        return btd_error_failed(client->reg, "Failed to parse advertisement.");
@@ -627,12 +830,12 @@ static struct btd_adv_client *client_create(struct btd_adv_manager *manager,
        if (!client->data)
                goto fail;
 
-       client->instance = util_get_uid(&manager->instance_bitmap,
-                                                       manager->max_ads);
-       if (!client->instance)
+       client->scan = bt_ad_new();
+       if (!client->scan)
                goto fail;
 
        client->manager = manager;
+       client->appearance = UINT16_MAX;
 
        return client;
 
@@ -675,6 +878,14 @@ static DBusMessage *register_advertisement(DBusConnection *conn,
                return btd_error_failed(msg,
                                        "Failed to register advertisement");
 
+       client->instance = util_get_uid(&manager->instance_bitmap,
+                                                       manager->max_ads);
+       if (!client->instance) {
+               client_free(client);
+               return btd_error_not_permitted(msg,
+                                       "Maximum advertisements reached");
+       }
+
        DBG("Registered advertisement at path %s", match.path);
 
        queue_push_tail(manager->clients, client);
@@ -712,12 +923,73 @@ static DBusMessage *unregister_advertisement(DBusConnection *conn,
        return dbus_message_new_method_return(msg);
 }
 
+static gboolean get_instances(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_adv_manager *manager = data;
+       uint8_t instances;
+
+       instances = manager->max_ads - queue_length(manager->clients);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances);
+
+       return TRUE;
+}
+
+static gboolean get_active_instances(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_adv_manager *manager = data;
+       uint8_t instances;
+
+       instances = queue_length(manager->clients);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &instances);
+
+       return TRUE;
+}
+
+static void append_include(struct btd_adv_manager *manager,
+                                               DBusMessageIter *iter)
+{
+       struct adv_include *inc;
+
+       for (inc = includes; inc && inc->name; inc++) {
+               if (manager->supported_flags & inc->flag)
+                       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+                                                               &inc->name);
+       }
+}
+
+static gboolean get_supported_includes(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct btd_adv_manager *manager = data;
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &entry);
+
+       append_include(manager, &entry);
+
+       dbus_message_iter_close_container(iter, &entry);
+
+       return TRUE;
+}
+
+static const GDBusPropertyTable properties[] = {
+       { "ActiveInstances", "y", get_active_instances, NULL, NULL },
+       { "SupportedInstances", "y", get_instances, NULL, NULL },
+       { "SupportedIncludes", "as", get_supported_includes, NULL, NULL },
+       { }
+};
+
 static const GDBusMethodTable methods[] = {
-       { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+       { GDBUS_ASYNC_METHOD("RegisterAdvertisement",
                                        GDBUS_ARGS({ "advertisement", "o" },
                                                        { "options", "a{sv}" }),
                                        NULL, register_advertisement) },
-       { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+       { GDBUS_ASYNC_METHOD("UnregisterAdvertisement",
                                                GDBUS_ARGS({ "service", "o" }),
                                                NULL,
                                                unregister_advertisement) },
@@ -753,16 +1025,24 @@ static void read_adv_features_callback(uint8_t status, uint16_t length,
        }
 
        manager->max_adv_len = feat->max_adv_data_len;
+       manager->max_scan_rsp_len = feat->max_scan_rsp_len;
        manager->max_ads = feat->max_instances;
+       manager->supported_flags |= feat->supported_flags;
 
        if (manager->max_ads == 0)
                return;
 
        if (!g_dbus_register_interface(btd_get_dbus_connection(),
                                        adapter_get_path(manager->adapter),
-                                       LE_ADVERTISING_MGR_IFACE,
-                                       methods, NULL, NULL, manager, NULL))
+                                       LE_ADVERTISING_MGR_IFACE, methods,
+                                       NULL, properties, manager, NULL)) {
                error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+               return;
+       }
+
+       /* Reset existing instances */
+       if (feat->num_instances)
+               remove_advertising(manager, 0);
 }
 
 static struct btd_adv_manager *manager_create(struct btd_adapter *adapter)
@@ -791,6 +1071,7 @@ static struct btd_adv_manager *manager_create(struct btd_adapter *adapter)
        }
 
        manager->clients = queue_new();
+       manager->supported_flags = MGMT_ADV_FLAG_LOCAL_NAME;
 
        return manager;
 }
index 2add5d7..037f8ba 100755 (executable)
@@ -48,6 +48,7 @@
     <allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
     <allow send_interface="org.bluez.GattCharacteristic1"/>
     <allow send_interface="org.bluez.GattDescriptor1"/>
+       <allow send_interface="org.bluez.LEAdvertisement1"/>
     <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
     <allow send_interface="org.freedesktop.DBus.Properties"/>
   </policy>
index 97ef3ec..d61dcc5 100755 (executable)
@@ -27,6 +27,10 @@ Print bluetoothd options and exit.
 Enable logging in foreground. Directs log output to the controlling terminal \
 in addition to syslog.
 .TP
+.B -f, --configfile
+Specifies an explicit config file path instead of relying on the default path \
+(@CONFIGDIR@/main.conf) for the config file.
+.TP
 .B -d, --debug=<file1>:<file2>:...
 Sets how much information bluetoothd sends to the log destination (usually \
 syslog's "daemon" facility). If the file options are omitted, then debugging \
index 9fd5e5b..7310053 100644 (file)
@@ -144,9 +144,15 @@ struct authentication_req {
        gboolean secure;
 };
 
+enum {
+       BROWSE_SDP,
+       BROWSE_GATT
+};
+
 struct browse_req {
        DBusMessage *msg;
        struct btd_device *device;
+       uint8_t type;
        GSList *match_uuids;
        GSList *profiles_added;
        sdp_list_t *records;
@@ -218,6 +224,8 @@ struct trusted_profile_t {
 struct btd_device {
        int ref_count;
 
+       bdaddr_t        conn_bdaddr;
+       uint8_t         conn_bdaddr_type;
        bdaddr_t        bdaddr;
        uint8_t         bdaddr_type;
        char            *path;
@@ -245,6 +253,7 @@ struct btd_device {
        GSList          *pending;               /* Pending services */
        GSList          *watches;               /* List of disconnect_data */
        bool            temporary;
+       bool            connectable;
        guint           disconn_timer;
        guint           discov_timer;
        struct browse_req *browse;              /* service discover request */
@@ -265,8 +274,10 @@ struct btd_device {
         * attribute cache support can be built.
         */
        struct gatt_db *db;                     /* GATT db cache */
+       unsigned int db_id;
        struct bt_gatt_client *client;          /* GATT client instance */
        struct bt_gatt_server *server;          /* GATT server instance */
+       unsigned int gatt_ready_id;
 
        struct btd_gatt_client *client_dbus;
 
@@ -739,6 +750,11 @@ static void browse_request_free(struct browse_req *req)
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        DBG("");
 #endif
+       struct btd_device *device = req->device;
+
+       if (device->browse == req)
+               device->browse = NULL;
+
        if (req->listener_id)
                g_dbus_remove_watch(dbus_conn, req->listener_id);
        if (req->msg)
@@ -750,13 +766,41 @@ static void browse_request_free(struct browse_req *req)
        g_free(req);
 }
 
+static bool gatt_cache_is_enabled(struct btd_device *device)
+{
+       switch (main_opts.gatt_cache) {
+       case BT_GATT_CACHE_YES:
+               return device_is_paired(device, device->bdaddr_type);
+       case BT_GATT_CACHE_NO:
+               return false;
+       case BT_GATT_CACHE_ALWAYS:
+       default:
+               return true;
+       }
+}
+
+static void gatt_cache_cleanup(struct btd_device *device)
+{
+       if (gatt_cache_is_enabled(device))
+               return;
+
+       gatt_db_clear(device->db);
+}
+
 static void gatt_client_cleanup(struct btd_device *device)
 {
        if (!device->client)
                return;
 
+       gatt_cache_cleanup(device);
        bt_gatt_client_set_service_changed(device->client, NULL, NULL, NULL);
-       bt_gatt_client_set_ready_handler(device->client, NULL, NULL, NULL);
+
+       if (device->gatt_ready_id > 0) {
+               bt_gatt_client_ready_unregister(device->client,
+                                               device->gatt_ready_id);
+               device->gatt_ready_id = 0;
+       }
+
        bt_gatt_client_unref(device->client);
        device->client = NULL;
 }
@@ -810,7 +854,6 @@ static void browse_request_cancel(struct browse_req *req)
 
        attio_cleanup(device);
 
-       device->browse = NULL;
        browse_request_free(req);
 }
 
@@ -838,7 +881,7 @@ static void device_free(gpointer user_data)
        g_slist_free_full(device->svc_callbacks, svc_dev_remove);
 
        /* Reset callbacks since the device is going to be freed */
-       gatt_db_register(device->db, NULL, NULL, NULL, NULL);
+       gatt_db_unregister(device->db, device->db_id);
 
        attio_cleanup(device);
 
@@ -974,12 +1017,28 @@ static gboolean dev_property_get_address(const GDBusPropertyTable *property,
                ba2str(device->rpa, dstaddr);
        else
 #endif
-               ba2str(&device->bdaddr, dstaddr);
+       ba2str(&device->bdaddr, dstaddr);
        dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
 
        return TRUE;
 }
 
+static gboolean property_get_address_type(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct btd_device *device = user_data;
+       const char *str;
+
+       if (device->le && device->bdaddr_type == BDADDR_LE_RANDOM)
+               str = "random";
+       else
+               str = "public";
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+       return TRUE;
+}
+
 static gboolean dev_property_get_name(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
@@ -1925,10 +1984,10 @@ static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
        struct btd_device *device = user_data;
 
        /*
-        * Disable connections through passive scanning until
-        * Device1.Connect is called
+        * If device is not trusted disable connections through passive
+        * scanning until Device1.Connect is called
         */
-       if (device->auto_connect) {
+       if (device->auto_connect && !device->trusted) {
                device->disable_auto_connect = TRUE;
                device_set_auto_connect(device, FALSE);
        }
@@ -2016,10 +2075,17 @@ done:
 
        l = find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED);
 
-       if (err && l == NULL)
+       if (err && l == NULL) {
+               /* Fallback to LE bearer if supported */
+               if (err == -EHOSTDOWN && dev->le && !dev->le_state.connected) {
+                       err = device_connect_le(dev);
+                       if (err == 0)
+                               return;
+               }
+
                g_dbus_send_message(dbus_conn,
                                btd_error_failed(dev->connect, strerror(-err)));
-       else {
+       else {
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                /* SDP is not required for Samsung TV Power on */
                if (g_strcmp0(profile->name, "hid-device") == 0) {
@@ -2082,8 +2148,12 @@ static void add_manufacturer_data(void *data, void *user_data)
                                        DEVICE_INTERFACE, "ManufacturerData");
 }
 
-void device_set_manufacturer_data(struct btd_device *dev, GSList *list)
+void device_set_manufacturer_data(struct btd_device *dev, GSList *list,
+                                                               bool duplicate)
 {
+       if (duplicate)
+               bt_ad_clear_manufacturer_data(dev->ad);
+
        g_slist_foreach(list, add_manufacturer_data, dev);
 }
 
@@ -2103,8 +2173,12 @@ static void add_service_data(void *data, void *user_data)
                                        DEVICE_INTERFACE, "ServiceData");
 }
 
-void device_set_service_data(struct btd_device *dev, GSList *list)
+void device_set_service_data(struct btd_device *dev, GSList *list,
+                                                       bool duplicate)
 {
+       if (duplicate)
+               bt_ad_clear_service_data(dev->ad);
+
        g_slist_foreach(list, add_service_data, dev);
 }
 
@@ -2763,6 +2837,9 @@ static void store_gatt_db(struct btd_device *device)
                return;
        }
 
+       if (!gatt_cache_is_enabled(device))
+               return;
+
        ba2str(btd_adapter_get_address(adapter), src_addr);
        ba2str(&device->bdaddr, dst_addr);
 
@@ -2789,12 +2866,15 @@ static void store_gatt_db(struct btd_device *device)
 }
 
 
-static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
-                                                                       int err)
+static void browse_request_complete(struct browse_req *req, uint8_t type,
+                                               uint8_t bdaddr_type, int err)
 {
        struct btd_device *dev = req->device;
        DBusMessage *reply = NULL;
 
+       if (req->type != type)
+               return;
+
        if (!req->msg)
                goto done;
 
@@ -2816,6 +2896,13 @@ static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
        }
 
        if (err) {
+               /* Fallback to LE bearer if supported */
+               if (err == -EHOSTDOWN && bdaddr_type == BDADDR_BREDR &&
+                               dev->le && !dev->le_state.connected) {
+                       err = device_connect_le(dev);
+                       if (err == 0)
+                               goto done;
+               }
                reply = btd_error_failed(req->msg, strerror(-err));
                goto done;
        }
@@ -2846,8 +2933,8 @@ static void device_set_svc_refreshed(struct btd_device *device, bool value)
                                        DEVICE_INTERFACE, "ServicesResolved");
 }
 
-static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
-                                                               int err)
+static void device_svc_resolved(struct btd_device *dev, uint8_t browse_type,
+                                               uint8_t bdaddr_type, int err)
 {
        struct bearer_state *state = get_state(dev, bdaddr_type);
        struct browse_req *req = dev->browse;
@@ -2910,9 +2997,7 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
                return;
        }
 #endif
-
-       dev->browse = NULL;
-       browse_request_complete(req, bdaddr_type, err);
+       browse_request_complete(req, browse_type, bdaddr_type, err);
 }
 
 static struct bonding_req *bonding_request_new(DBusMessage *msg,
@@ -4181,6 +4266,7 @@ static const GDBusMethodTable device_methods[] = {
 
 static const GDBusPropertyTable device_properties[] = {
        { "Address", "s", dev_property_get_address },
+       { "AddressType", "s", property_get_address_type },
        { "Name", "s", dev_property_get_name, NULL, dev_property_exists_name },
        { "Alias", "s", dev_property_get_alias, dev_property_set_alias },
        { "Class", "u", dev_property_get_class, NULL,
@@ -4277,6 +4363,9 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
                return;
        }
 
+       bacpy(&dev->conn_bdaddr, &dev->bdaddr);
+       dev->conn_bdaddr_type = dev->bdaddr_type;
+
        /* If this is the first connection over this bearer */
        if (bdaddr_type == BDADDR_BREDR)
                device_set_bredr_support(dev);
@@ -5077,6 +5166,9 @@ static void load_gatt_db(struct btd_device *device, const char *local,
        char **keys, filename[PATH_MAX];
        GKeyFile *key_file;
 
+       if (!gatt_cache_is_enabled(device))
+               return;
+
        DBG("Restoring %s gatt database from file", peer);
 
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
@@ -5442,8 +5534,8 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
        device->adapter = adapter;
        device->temporary = true;
 
-       gatt_db_register(device->db, gatt_service_added, gatt_service_removed,
-                                                               device, NULL);
+       device->db_id = gatt_db_register(device->db, gatt_service_added,
+                                       gatt_service_removed, device, NULL);
 
        return btd_device_ref(device);
 }
@@ -5624,6 +5716,8 @@ void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "Address");
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "AddressType");
 }
 
 void device_set_bredr_support(struct btd_device *device)
@@ -5961,7 +6055,8 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
        device->pending = NULL;
 
        if (btd_device_is_connected(device)) {
-               g_source_remove(device->disconn_timer);
+               if (device->disconn_timer > 0)
+                       g_source_remove(device->disconn_timer);
                disconnect_all(device);
        }
 
@@ -6099,8 +6194,11 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b)
        if (!dev->le)
                return -1;
 
-       if (addr->bdaddr_type != dev->bdaddr_type)
+       if (addr->bdaddr_type != dev->bdaddr_type) {
+               if (addr->bdaddr_type == dev->conn_bdaddr_type)
+                       return bacmp(&dev->conn_bdaddr, &addr->bdaddr);
                return -1;
+       }
 
        return cmp;
 }
@@ -6672,7 +6770,7 @@ send_reply:
 done:
 #endif
 
-       device_svc_resolved(device, BDADDR_BREDR, err);
+       device_svc_resolved(device, BROWSE_SDP, BDADDR_BREDR, err);
 }
 
 static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
@@ -6829,7 +6927,8 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
        DBG("status: %s, error: %u", success ? "success" : "failed", att_ecode);
 
        if (!success) {
-               device_svc_resolved(device, device->bdaddr_type, -EIO);
+               device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type,
+                                                                       -EIO);
                return;
        }
 
@@ -6845,7 +6944,7 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
        register_gatt_services(device);
 #endif
 
-       device_svc_resolved(device, device->bdaddr_type, 0);
+       device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type, 0);
 
        store_gatt_db(device);
 
@@ -6890,10 +6989,11 @@ static void gatt_client_init(struct btd_device *device)
         */
        device_accept_gatt_profiles(device);
 
-       if (!bt_gatt_client_set_ready_handler(device->client,
+       device->gatt_ready_id = bt_gatt_client_ready_register(device->client,
                                                        gatt_client_ready_cb,
-                                                       device, NULL)) {
-               DBG("Failed to set ready handler");
+                                                       device, NULL);
+       if (!device->gatt_ready_id) {
+               DBG("Failed to register GATT ready callback");
                gatt_client_cleanup(device);
                return;
        }
@@ -7116,12 +7216,11 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
                        adapter_connect_list_add(device->adapter, device);
                }
 
-               if (device->browse) {
+               if (device->browse)
                        browse_request_complete(device->browse,
+                                               BROWSE_GATT,
                                                device->bdaddr_type,
                                                -ECONNABORTED);
-                       device->browse = NULL;
-               }
 
                err = -ECONNABORTED;
                goto done;
@@ -7222,6 +7321,7 @@ int device_connect_le(struct btd_device *dev)
 }
 
 static struct browse_req *browse_request_new(struct btd_device *device,
+                                                       uint8_t type,
                                                        DBusMessage *msg)
 {
        struct browse_req *req;
@@ -7231,6 +7331,7 @@ static struct browse_req *browse_request_new(struct btd_device *device,
 
        req = g_new0(struct browse_req, 1);
        req->device = device;
+       req->type = type;
 
        device->browse = req;
 
@@ -7259,7 +7360,7 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        DBG("");
 #endif
-       req = browse_request_new(device, msg);
+       req = browse_request_new(device, BROWSE_GATT, msg);
        if (!req)
                return -EBUSY;
 
@@ -7275,7 +7376,8 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
                 * Services have already been discovered, so signal this browse
                 * request as resolved.
                 */
-               device_svc_resolved(device, device->bdaddr_type, 0);
+               device_svc_resolved(device, BROWSE_GATT, device->bdaddr_type,
+                                                                       0);
                return 0;
        }
 
@@ -7292,7 +7394,6 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
                                BT_IO_OPT_INVALID);
 
        if (device->att_io == NULL) {
-               device->browse = NULL;
                browse_request_free(req);
                return -EIO;
        }
@@ -7335,7 +7436,7 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        DBG("");
 #endif
-       req = browse_request_new(device, msg);
+       req = browse_request_new(device, BROWSE_SDP, msg);
        if (!req)
                return -EBUSY;
 
@@ -7740,6 +7841,18 @@ void device_set_flags(struct btd_device *device, uint8_t flags)
                                        DEVICE_INTERFACE, "AdvertisingFlags");
 }
 
+bool device_is_connectable(struct btd_device *device)
+{
+       if (!device)
+               return false;
+
+       if (device->bredr)
+               return true;
+
+       /* Check if either Limited or General discoverable are set */
+       return (device->ad_flags[0] & 0x03);
+}
+
 static gboolean start_discovery(gpointer user_data)
 {
        struct btd_device *device = user_data;
@@ -7763,7 +7876,7 @@ void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type)
 
        state->paired = true;
 
-       /* If the other bearer state was alraedy true we don't need to
+       /* If the other bearer state was already true we don't need to
         * send any property signals.
         */
        if (dev->bredr_state.paired == dev->le_state.paired)
@@ -8530,6 +8643,39 @@ static sdp_list_t *read_device_records(struct btd_device *device)
        return recs;
 }
 
+void btd_device_set_record(struct btd_device *device, const char *uuid,
+                                                       const char *record)
+{
+       /* This API is only used for BR/EDR */
+       struct bearer_state *state = &device->bredr_state;
+       struct browse_req *req;
+       sdp_list_t *recs = NULL;
+       sdp_record_t *rec;
+
+       if (!record)
+               return;
+
+       req = browse_request_new(device, BROWSE_SDP, NULL);
+       if (!req)
+               return;
+
+       rec = record_from_string(record);
+       recs = sdp_list_append(recs, rec);
+       update_bredr_services(req, recs);
+       sdp_list_free(recs, NULL);
+
+       device->svc_refreshed = true;
+       state->svc_resolved = true;
+
+       device_probe_profiles(device, req->profiles_added);
+
+       /* Propagate services changes */
+       g_dbus_emit_property_changed(dbus_conn, req->device->path,
+                                               DEVICE_INTERFACE, "UUIDs");
+
+       device_svc_resolved(device, BROWSE_SDP, device->bdaddr_type, 0);
+}
+
 const sdp_record_t *btd_device_get_record(struct btd_device *device,
                                                        const char *uuid)
 {
index 55aea7c..4f62671 100644 (file)
@@ -72,6 +72,9 @@ int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b);
 int device_addr_type_cmp(gconstpointer a, gconstpointer b);
 GSList *btd_device_get_uuids(struct btd_device *device);
 void device_probe_profiles(struct btd_device *device, GSList *profiles);
+
+void btd_device_set_record(struct btd_device *device, const char *uuid,
+                                                       const char *record);
 const sdp_record_t *btd_device_get_record(struct btd_device *device,
                                                const char *uuid);
 struct gatt_primary *btd_device_get_primary(struct btd_device *device,
@@ -86,8 +89,10 @@ void btd_device_gatt_set_service_changed(struct btd_device *device,
 bool device_attach_att(struct btd_device *dev, GIOChannel *io);
 void btd_device_add_uuid(struct btd_device *device, const char *uuid);
 void device_add_eir_uuids(struct btd_device *dev, GSList *uuids);
-void device_set_manufacturer_data(struct btd_device *dev, GSList *list);
-void device_set_service_data(struct btd_device *dev, GSList *list);
+void device_set_manufacturer_data(struct btd_device *dev, GSList *list,
+                                                       bool duplicate);
+void device_set_service_data(struct btd_device *dev, GSList *list,
+                                                       bool duplicate);
 void device_probe_profile(gpointer a, gpointer b);
 void device_remove_profile(gpointer a, gpointer b);
 struct btd_adapter *device_get_adapter(struct btd_device *device);
@@ -115,6 +120,7 @@ void btd_device_set_trusted_profiles(struct btd_device *device,
                uint32_t hfp_hs, uint32_t a2dp);
 #endif
 gboolean device_is_temporary(struct btd_device *device);
+bool device_is_connectable(struct btd_device *device);
 bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type);
 bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
 gboolean device_is_trusted(struct btd_device *device);
index 05379ee..a3f3765 100644 (file)
@@ -24,6 +24,8 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include <dbus/dbus.h>
 
@@ -37,6 +39,7 @@
 #include "error.h"
 #include "adapter.h"
 #include "device.h"
+#include "src/shared/io.h"
 #include "src/shared/queue.h"
 #include "src/shared/att.h"
 #include "src/shared/gatt-db.h"
@@ -62,6 +65,7 @@ struct btd_gatt_client {
 
        struct queue *services;
        struct queue *all_notify_clients;
+       struct queue *ios;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        guint wait_charcs_id;
 #endif
@@ -75,6 +79,7 @@ struct service {
        bt_uuid_t uuid;
        char *path;
        struct queue *chrcs;
+       struct queue *incl_services;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        bool chrcs_ready;
        struct queue *pending_ext_props;
@@ -93,6 +98,13 @@ struct async_dbus_op {
        async_dbus_op_complete_t complete;
 };
 
+struct pipe_io {
+       DBusMessage *msg;
+       struct io *io;
+       void (*destroy)(void *data);
+       void *data;
+};
+
 struct characteristic {
        struct service *service;
        struct gatt_db_attribute *attr;
@@ -104,6 +116,10 @@ struct characteristic {
        bt_uuid_t uuid;
        char *path;
 
+       unsigned int ready_id;
+       struct pipe_io *write_io;
+       struct pipe_io *notify_io;
+
        struct async_dbus_op *read_op;
        struct async_dbus_op *write_op;
 
@@ -344,7 +360,6 @@ static void async_dbus_op_reply(struct async_dbus_op *op, int err,
                if (err) {
                        reply = err > 0 ? create_gatt_dbus_error(msg, err) :
                                btd_error_failed(msg, strerror(-err));
-
                        goto send_reply;
                }
 
@@ -638,6 +653,8 @@ static DBusMessage *descriptor_write_value(DBusConnection *conn,
        if (parse_value_arg(&iter, &value, &value_len))
                return btd_error_invalid_args(msg);
 
+       dbus_message_iter_next(&iter);
+
        if (parse_options(&iter, &offset))
                return btd_error_invalid_args(msg);
 
@@ -868,6 +885,48 @@ static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+static gboolean
+characteristic_get_write_acquired(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct characteristic *chrc = data;
+       dbus_bool_t locked = chrc->write_io ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+       return TRUE;
+}
+
+static gboolean
+characteristic_write_acquired_exists(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct characteristic *chrc = data;
+
+       return (chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP);
+}
+
+static gboolean
+characteristic_get_notify_acquired(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct characteristic *chrc = data;
+       dbus_bool_t locked = chrc->notify_io ? TRUE : FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &locked);
+
+       return TRUE;
+}
+
+static gboolean
+characteristic_notify_acquired_exists(const GDBusPropertyTable *property,
+                                                               void *data)
+{
+       struct characteristic *chrc = data;
+
+       return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY);
+}
+
 static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
                                                                void *user_data)
 {
@@ -986,6 +1045,9 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
        if (!gatt)
                return btd_error_failed(msg, "Not connected");
 
+       if (chrc->write_io)
+               return btd_error_not_permitted(msg, "Write acquired");
+
        if (chrc->write_op)
                return btd_error_in_progress(msg);
 
@@ -994,6 +1056,8 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
        if (parse_value_arg(&iter, &value, &value_len))
                return btd_error_invalid_args(msg);
 
+       dbus_message_iter_next(&iter);
+
        if (parse_options(&iter, &offset))
                return btd_error_invalid_args(msg);
 
@@ -1060,6 +1124,197 @@ fail:
        return btd_error_not_supported(msg);
 }
 
+static bool chrc_pipe_read(struct io *io, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+       uint8_t buf[512];
+       int fd = io_get_fd(io);
+       ssize_t bytes_read;
+
+       bytes_read = read(fd, buf, sizeof(buf));
+       if (bytes_read < 0)
+               return false;
+
+       if (!gatt)
+               return false;
+
+       bt_gatt_client_write_without_response(gatt, chrc->value_handle,
+                                       chrc->props & BT_GATT_CHRC_PROP_AUTH,
+                                       buf, bytes_read);
+
+       return true;
+}
+
+static void pipe_io_destroy(struct pipe_io *io)
+{
+       if (io->destroy)
+               io->destroy(io->data);
+
+       if (io->msg)
+               dbus_message_unref(io->msg);
+
+       io_destroy(io->io);
+       free(io);
+}
+
+static void characteristic_destroy_pipe(struct characteristic *chrc,
+                                                       struct io *io)
+{
+       queue_remove(chrc->service->client->ios, io);
+
+       if (chrc->write_io && io == chrc->write_io->io) {
+               pipe_io_destroy(chrc->write_io);
+               chrc->write_io = NULL;
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                               chrc->path,
+                                               GATT_CHARACTERISTIC_IFACE,
+                                               "WriteAcquired");
+       } else if (chrc->notify_io) {
+               pipe_io_destroy(chrc->notify_io);
+               chrc->notify_io = NULL;
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                               chrc->path,
+                                               GATT_CHARACTERISTIC_IFACE,
+                                               "NotifyAcquired");
+       }
+}
+
+static bool characteristic_pipe_hup(struct io *io, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+
+       DBG("%s: io %p", chrc->path, io);
+
+       characteristic_destroy_pipe(chrc, io);
+
+       return false;
+}
+
+static DBusMessage *characteristic_create_pipe(struct characteristic *chrc,
+                                               DBusMessage *msg)
+{
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+       int pipefd[2];
+       struct io *io;
+       bool dir;
+       uint16_t mtu;
+       DBusMessage *reply;
+
+       if (!gatt || !bt_gatt_client_is_ready(gatt))
+               return btd_error_failed(msg, "Not connected");
+
+       if (pipe2(pipefd, O_DIRECT | O_NONBLOCK | O_CLOEXEC) < 0)
+               return btd_error_failed(msg, strerror(errno));
+
+       dir = dbus_message_has_member(msg, "AcquireWrite");
+
+       io = io_new(pipefd[!dir]);
+       if (!io) {
+               close(pipefd[0]);
+               close(pipefd[1]);
+               return btd_error_failed(msg, strerror(EIO));
+       }
+
+       io_set_close_on_destroy(io, true);
+
+       if (!io_set_read_handler(io, chrc_pipe_read, chrc, NULL))
+               goto fail;
+
+       if (!io_set_disconnect_handler(io, characteristic_pipe_hup, chrc, NULL))
+               goto fail;
+
+       mtu = bt_gatt_client_get_mtu(gatt);
+
+       reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &pipefd[dir],
+                                       DBUS_TYPE_UINT16, &mtu,
+                                       DBUS_TYPE_INVALID);
+
+       close(pipefd[dir]);
+
+       if (dir) {
+               chrc->write_io->io = io;
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                               chrc->path,
+                                               GATT_CHARACTERISTIC_IFACE,
+                                               "WriteAcquired");
+       } else {
+               chrc->notify_io->io = io;
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                               chrc->path,
+                                               GATT_CHARACTERISTIC_IFACE,
+                                               "NotifyAcquired");
+       }
+
+       queue_push_tail(chrc->service->client->ios, io);
+
+       DBG("%s: sender %s io %p", dbus_message_get_member(msg),
+                                       dbus_message_get_sender(msg), io);
+
+       return reply;
+
+fail:
+       io_destroy(io);
+       close(pipefd[dir]);
+       return btd_error_failed(msg, strerror(EIO));
+}
+
+static void characteristic_ready(bool success, uint8_t ecode, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       DBusMessage *reply;
+
+       chrc->ready_id = 0;
+
+       if (chrc->write_io && chrc->write_io->msg) {
+               reply = characteristic_create_pipe(chrc, chrc->write_io->msg);
+
+               g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+               dbus_message_unref(chrc->write_io->msg);
+               chrc->write_io->msg = NULL;
+       }
+
+       if (chrc->notify_io && chrc->notify_io->msg) {
+               reply = characteristic_create_pipe(chrc, chrc->notify_io->msg);
+
+               g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+               dbus_message_unref(chrc->notify_io->msg);
+               chrc->notify_io->msg = NULL;
+       }
+}
+
+static DBusMessage *characteristic_acquire_write(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+       if (!gatt)
+               return btd_error_failed(msg, "Not connected");
+
+       if (chrc->write_io)
+               return btd_error_not_permitted(msg, "Write acquired");
+
+       if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+               return btd_error_not_supported(msg);
+
+       chrc->write_io = new0(struct pipe_io, 1);
+
+       if (!bt_gatt_client_is_ready(gatt)) {
+               /* GATT not ready, wait until it becomes ready */
+               if (!chrc->ready_id)
+                       chrc->ready_id = bt_gatt_client_ready_register(gatt,
+                                                       characteristic_ready,
+                                                       chrc, NULL);
+               chrc->write_io->msg = dbus_message_ref(msg);
+               return NULL;
+       }
+
+       return characteristic_create_pipe(chrc, msg);
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
@@ -1326,6 +1581,100 @@ static void register_notify_cb(uint16_t att_ecode, void *user_data)
        create_notify_reply(op, true, 0);
 }
 
+static void notify_io_cb(uint16_t value_handle, const uint8_t *value,
+                                       uint16_t length, void *user_data)
+{
+       struct iovec iov;
+       struct notify_client *client = user_data;
+       struct characteristic *chrc = client->chrc;
+       int err;
+
+       /* Drop notification if the pipe is not ready */
+       if (!chrc->notify_io->io)
+               return;
+
+       iov.iov_base = (void *) value;
+       iov.iov_len = length;
+
+       err = io_send(chrc->notify_io->io, &iov, 1);
+       if (err < 0)
+               error("io_send: %s", strerror(-err));
+}
+
+static void register_notify_io_cb(uint16_t att_ecode, void *user_data)
+{
+       struct notify_client *client = user_data;
+       struct characteristic *chrc = client->chrc;
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+
+       if (att_ecode) {
+               queue_remove(chrc->notify_clients, client);
+               notify_client_free(client);
+               return;
+       }
+
+       if (!bt_gatt_client_is_ready(gatt)) {
+               if (!chrc->ready_id)
+                       chrc->ready_id = bt_gatt_client_ready_register(gatt,
+                                                       characteristic_ready,
+                                                       chrc, NULL);
+               return;
+       }
+
+       characteristic_ready(true, 0, chrc);
+}
+
+static void notify_io_destroy(void *data)
+{
+       struct notify_client *client = data;
+
+       if (queue_remove(client->chrc->notify_clients, client))
+               notify_client_unref(client);
+}
+
+static DBusMessage *characteristic_acquire_notify(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct characteristic *chrc = user_data;
+       struct bt_gatt_client *gatt = chrc->service->client->gatt;
+       const char *sender = dbus_message_get_sender(msg);
+       struct notify_client *client;
+
+       if (!gatt)
+               return btd_error_failed(msg, "Not connected");
+
+       if (chrc->notify_io)
+               return btd_error_not_permitted(msg, "Notify acquired");
+
+       /* Each client can only have one active notify session. */
+       if (!queue_isempty(chrc->notify_clients))
+               return btd_error_in_progress(msg);
+
+       if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY))
+               return btd_error_not_supported(msg);
+
+       client = notify_client_create(chrc, sender);
+       if (!client)
+               return btd_error_failed(msg, "Failed allocate notify session");
+
+       client->notify_id = bt_gatt_client_register_notify(gatt,
+                                               chrc->value_handle,
+                                               register_notify_io_cb,
+                                               notify_io_cb,
+                                               client, NULL);
+       if (!client->notify_id)
+               return btd_error_failed(msg, "Failed to subscribe");
+
+       queue_push_tail(chrc->notify_clients, client);
+
+       chrc->notify_io = new0(struct pipe_io, 1);
+       chrc->notify_io->data = client;
+       chrc->notify_io->msg = dbus_message_ref(msg);
+       chrc->notify_io->destroy = notify_io_destroy;
+
+       return NULL;
+}
+
 static DBusMessage *characteristic_start_notify(DBusConnection *conn,
                                        DBusMessage *msg, void *user_data)
 {
@@ -1335,6 +1684,9 @@ static DBusMessage *characteristic_start_notify(DBusConnection *conn,
        struct async_dbus_op *op;
        struct notify_client *client;
 
+       if (chrc->notify_io)
+               return btd_error_not_permitted(msg, "Notify acquired");
+
        if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
                                chrc->props & BT_GATT_CHRC_PROP_INDICATE))
                return btd_error_not_supported(msg);
@@ -1343,7 +1695,7 @@ static DBusMessage *characteristic_start_notify(DBusConnection *conn,
        client = queue_find(chrc->notify_clients, match_notify_sender, sender);
        if (client)
                return client->notify_id ?
-                               btd_error_failed(msg, "Already notifying") :
+                               g_dbus_create_reply(msg, DBUS_TYPE_INVALID) :
                                btd_error_in_progress(msg);
 
        client = notify_client_create(chrc, sender);
@@ -1407,6 +1759,11 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
        if (!client)
                return btd_error_failed(msg, "No notify session started");
 
+       if (chrc->notify_io) {
+               characteristic_destroy_pipe(chrc, chrc->notify_io->io);
+               return dbus_message_new_method_return(msg);
+       }
+
        queue_remove(chrc->service->client->all_notify_clients, client);
        bt_gatt_client_unregister_notify(gatt, client->notify_id);
        update_notifying(chrc);
@@ -1455,6 +1812,10 @@ static const GDBusPropertyTable characteristic_properties[] = {
        { "Notifying", "b", characteristic_get_notifying, NULL,
                                        characteristic_notifying_exists },
        { "Flags", "as", characteristic_get_flags, NULL, NULL },
+       { "WriteAcquired", "b", characteristic_get_write_acquired, NULL,
+                               characteristic_write_acquired_exists },
+       { "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL,
+                               characteristic_notify_acquired_exists },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        { "Descriptors", "ao", characteristic_get_descriptors },
 #endif
@@ -1469,6 +1830,16 @@ static const GDBusMethodTable characteristic_methods[] = {
                                                { "options", "a{sv}" }),
                                        NULL,
                                        characteristic_write_value) },
+       { GDBUS_ASYNC_METHOD("AcquireWrite",
+                                       GDBUS_ARGS({ "options", "a{sv}" }),
+                                       GDBUS_ARGS({ "fd", "h" },
+                                               { "mtu", "q" }),
+                                       characteristic_acquire_write) },
+       { GDBUS_ASYNC_METHOD("AcquireNotify",
+                                       GDBUS_ARGS({ "options", "a{sv}" }),
+                                       GDBUS_ARGS({ "fd", "h" },
+                                               { "mtu", "q" }),
+                                       characteristic_acquire_notify) },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        { GDBUS_ASYNC_METHOD("WriteValuebyType",
                                        GDBUS_ARGS({ "type", "y" }, { "value", "ay" },
@@ -1504,6 +1875,16 @@ static void characteristic_free(void *data)
        queue_destroy(chrc->descs, NULL);
        queue_destroy(chrc->notify_clients, NULL);
 
+       if (chrc->write_io) {
+               queue_remove(chrc->service->client->ios, chrc->write_io->io);
+               pipe_io_destroy(chrc->write_io);
+       }
+
+       if (chrc->notify_io) {
+               queue_remove(chrc->service->client->ios, chrc->notify_io->io);
+               pipe_io_destroy(chrc->notify_io);
+       }
+
        g_free(chrc->path);
        free(chrc);
 }
@@ -1640,6 +2021,31 @@ static gboolean service_get_primary(const GDBusPropertyTable *property,
        return TRUE;
 }
 
+static void append_incl_service_path(void *data, void *user_data)
+{
+       struct service *incl_service = data;
+       DBusMessageIter *array = user_data;
+
+       dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+                                       &incl_service->path);
+}
+
+static gboolean service_get_includes(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct service *service = data;
+       DBusMessageIter array;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{o}", &array);
+
+       queue_foreach(service->incl_services, append_incl_service_path, &array);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void append_chrc_path(void *data, void *user_data)
 {
@@ -1671,6 +2077,7 @@ static const GDBusPropertyTable service_properties[] = {
        { "UUID", "s", service_get_uuid },
        { "Device", "o", service_get_device },
        { "Primary", "b", service_get_primary },
+       { "Includes", "ao", service_get_includes },
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        { "Characteristics", "ao", service_get_characteristics },
 #endif
@@ -1682,6 +2089,7 @@ static void service_free(void *data)
        struct service *service = data;
 
        queue_destroy(service->chrcs, NULL);  /* List should be empty here */
+       queue_destroy(service->incl_services, NULL);
        g_free(service->path);
        free(service);
 }
@@ -1695,6 +2103,7 @@ static struct service *service_create(struct gatt_db_attribute *attr,
 
        service = new0(struct service, 1);
        service->chrcs = queue_new();
+       service->incl_services = queue_new();
        service->client = client;
 
        gatt_db_attribute_get_service_data(attr, &service->start_handle,
@@ -1735,9 +2144,22 @@ static struct service *service_create(struct gatt_db_attribute *attr,
        return service;
 }
 
+static void on_service_removed(void *data, void *user_data)
+{
+       struct service *service = data;
+       struct service *removed_service = user_data;
+
+       if (queue_remove(service->incl_services, removed_service))
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                                       service->path,
+                                                       GATT_SERVICE_IFACE,
+                                                       "Includes");
+}
+
 static void unregister_service(void *data)
 {
        struct service *service = data;
+       struct btd_gatt_client *client = service->client;
 
        DBG("Removing GATT service: %s", service->path);
 
@@ -1747,6 +2169,9 @@ static void unregister_service(void *data)
 #endif
 
        queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
+       queue_remove_all(service->incl_services, NULL, NULL, NULL);
+
+       queue_foreach(client->services, on_service_removed, service);
 
        g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
                                                        GATT_SERVICE_IFACE);
@@ -1879,11 +2304,9 @@ static bool create_characteristics(struct gatt_db_attribute *attr,
 
        gatt_db_service_foreach_char(attr, export_char, &data);
 
-       if (data.failed)
-               return false;
-
-       return true;
+       return !data.failed;
 }
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static gboolean set_chrcs_ready(gpointer user_data)
 {
@@ -1926,11 +2349,71 @@ static void export_service(struct gatt_db_attribute *attr, void *user_data)
 #endif
 }
 
+static bool match_service_handle(const void *a, const void *b)
+{
+       const struct service *service = a;
+       uint16_t start_handle = PTR_TO_UINT(b);
+
+       return service->start_handle == start_handle;
+}
+
+struct update_incl_data {
+       struct service *service;
+       bool changed;
+};
+
+static void update_included_service(struct gatt_db_attribute *attrib,
+                                                       void *user_data)
+{
+       struct update_incl_data *update_data = user_data;
+       struct btd_gatt_client *client = update_data->service->client;
+       struct service *service = update_data->service;
+       struct service *incl_service;
+       uint16_t start_handle;
+
+       gatt_db_attribute_get_incl_data(attrib, NULL, &start_handle, NULL);
+
+       incl_service = queue_find(client->services, match_service_handle,
+                                               UINT_TO_PTR(start_handle));
+
+       if (!incl_service)
+               return;
+
+       /* Check if service is already on list */
+       if (queue_find(service->incl_services, NULL, incl_service))
+               return;
+
+       queue_push_tail(service->incl_services, incl_service);
+       update_data->changed = true;
+}
+
+static void update_included_services(void *data, void *user_data)
+{
+       struct btd_gatt_client *client = user_data;
+       struct service *service = data;
+       struct gatt_db_attribute *attr;
+       struct update_incl_data inc_data = {
+               .changed = false,
+               .service = service,
+       };
+
+       attr = gatt_db_get_attribute(client->db, service->start_handle);
+       gatt_db_service_foreach_incl(attr, update_included_service, &inc_data);
+
+       if (inc_data.changed)
+               g_dbus_emit_property_changed(btd_get_dbus_connection(),
+                                                       service->path,
+                                                       GATT_SERVICE_IFACE,
+                                                       "Includes");
+}
+
 static void create_services(struct btd_gatt_client *client)
 {
        DBG("Exporting objects for GATT services: %s", client->devaddr);
 
        gatt_db_foreach_service(client->db, NULL, export_service, client);
+
+       queue_foreach(client->services, update_included_services, client);
 }
 
 struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
@@ -1948,6 +2431,7 @@ struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
        client = new0(struct btd_gatt_client, 1);
        client->services = queue_new();
        client->all_notify_clients = queue_new();
+       client->ios = queue_new();
        client->device = device;
        ba2str(device_get_address(device), client->devaddr);
 
@@ -1967,6 +2451,7 @@ void btd_gatt_client_destroy(struct btd_gatt_client *client)
 #endif
        queue_destroy(client->services, unregister_service);
        queue_destroy(client->all_notify_clients, NULL);
+       queue_destroy(client->ios, NULL);
        bt_gatt_client_unref(client->gatt);
        gatt_db_unref(client->db);
        free(client);
@@ -2126,14 +2611,8 @@ void btd_gatt_client_service_added(struct btd_gatt_client *client,
                return;
 
        export_service(attrib, client);
-}
-
-static bool match_service_handle(const void *a, const void *b)
-{
-       const struct service *service = a;
-       uint16_t start_handle = PTR_TO_UINT(b);
 
-       return service->start_handle == start_handle;
+       queue_foreach(client->services, update_included_services, client);
 }
 
 void btd_gatt_client_service_removed(struct btd_gatt_client *client,
@@ -2174,6 +2653,8 @@ void btd_gatt_client_disconnected(struct btd_gatt_client *client)
                client->wait_charcs_id = 0;
        }
 #endif
+       queue_remove_all(client->ios, NULL, NULL,
+                               (queue_destroy_func_t) io_shutdown);
 
        /*
         * TODO: Once GATT over BR/EDR is properly supported, we should pass the
index ee3b873..0ec136d 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <unistd.h>
 
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
@@ -33,6 +34,7 @@
 #include "gdbus/gdbus.h"
 #include "src/shared/util.h"
 #include "src/shared/queue.h"
+#include "src/shared/io.h"
 #include "src/shared/att.h"
 #include "src/shared/gatt-db.h"
 #include "src/shared/gatt-server.h"
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #endif
 
+struct gatt_record {
+       struct btd_gatt_database *database;
+       uint32_t handle;
+       struct gatt_db_attribute *attr;
+};
+
 struct btd_gatt_database {
        struct btd_adapter *adapter;
        struct gatt_db *db;
        unsigned int db_id;
        GIOChannel *le_io;
        GIOChannel *l2cap_io;
-       uint32_t gap_handle;
-       uint32_t gatt_handle;
+       struct queue *records;
        struct queue *device_states;
        struct queue *ccc_callbacks;
        struct gatt_db_attribute *svc_chngd;
@@ -117,6 +124,9 @@ struct external_chrc {
        uint8_t props;
        uint8_t ext_props;
        uint32_t perm;
+       uint16_t mtu;
+       struct io *write_io;
+       struct io *notify_io;
        struct gatt_db_attribute *attrib;
        struct gatt_db_attribute *ccc;
        struct queue *pending_reads;
@@ -138,21 +148,23 @@ struct external_desc {
 struct pending_op {
        struct btd_device *device;
        unsigned int id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        uint16_t offset;
-#endif
+       uint8_t link_type;
        struct gatt_db_attribute *attrib;
        struct queue *owner_queue;
        struct iovec data;
 };
 
 struct device_state {
+       struct btd_gatt_database *db;
        bdaddr_t bdaddr;
        uint8_t bdaddr_type;
+       unsigned int disc_id;
        struct queue *ccc_states;
 };
 
-typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (struct bt_att *att,
+                                                       uint16_t value,
                                                        void *user_data);
 typedef void (*btd_gatt_database_destroy_t) (void *data);
 
@@ -230,6 +242,121 @@ find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
        return queue_find(database->device_states, dev_state_match, &dev_info);
 }
 
+static bool ccc_state_match(const void *a, const void *b)
+{
+       const struct ccc_state *ccc = a;
+       uint16_t handle = PTR_TO_UINT(b);
+
+       return ccc->handle == handle;
+}
+
+static struct ccc_state *find_ccc_state(struct device_state *dev_state,
+                                                               uint16_t handle)
+{
+       return queue_find(dev_state->ccc_states, ccc_state_match,
+                                                       UINT_TO_PTR(handle));
+}
+
+static struct device_state *device_state_create(struct btd_gatt_database *db,
+                                                       bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       struct device_state *dev_state;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       char addr[18] = { 0 };
+
+       ba2str(bdaddr, addr);
+       DBG("create device_state for %s [%d]", addr, bdaddr_type);
+#endif
+
+       dev_state = new0(struct device_state, 1);
+       dev_state->db = db;
+       dev_state->ccc_states = queue_new();
+       bacpy(&dev_state->bdaddr, bdaddr);
+       dev_state->bdaddr_type = bdaddr_type;
+
+       return dev_state;
+}
+
+static void device_state_free(void *data)
+{
+       struct device_state *state = data;
+
+       queue_destroy(state->ccc_states, free);
+       free(state);
+}
+
+static void clear_ccc_state(void *data, void *user_data)
+{
+       struct ccc_state *ccc = data;
+       struct btd_gatt_database *db = user_data;
+       struct ccc_cb_data *ccc_cb;
+
+       if (!ccc->value[0])
+               return;
+
+       ccc_cb = queue_find(db->ccc_callbacks, ccc_cb_match_handle,
+                                               UINT_TO_PTR(ccc->handle));
+       if (!ccc_cb)
+               return;
+
+       if (ccc_cb->callback)
+               ccc_cb->callback(NULL, 0, ccc_cb->user_data);
+}
+
+static void att_disconnected(int err, void *user_data)
+{
+       struct device_state *state = user_data;
+       struct btd_device *device;
+
+       DBG("");
+
+       state->disc_id = 0;
+
+       device = btd_adapter_get_device(state->db->adapter, &state->bdaddr,
+                                       state->bdaddr_type);
+       if (!device)
+               goto remove;
+
+       if (device_is_paired(device, state->bdaddr_type))
+               return;
+
+remove:
+       /* Remove device state if device no longer exists or is not paired */
+       if (queue_remove(state->db->device_states, state)) {
+               queue_foreach(state->ccc_states, clear_ccc_state, state->db);
+               device_state_free(state);
+       }
+}
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       return bt_att_get_remote_addr(att, dst, dst_type);
+#else
+       GIOChannel *io = NULL;
+       GError *gerr = NULL;
+
+       io = g_io_channel_unix_new(bt_att_get_fd(att));
+       if (!io)
+               return false;
+
+       bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+                                               BT_IO_OPT_DEST_TYPE, dst_type,
+                                               BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("gatt: bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_unref(io);
+               return false;
+       }
+
+       g_io_channel_unref(io);
+
+       return true;
+#endif
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void send_service_changed_indication_on_reconnect(
                                        struct btd_device *device,
@@ -280,84 +407,51 @@ find_device_state_from_address(struct btd_gatt_database *database, const bdaddr_
 }
 #endif
 
-static bool ccc_state_match(const void *a, const void *b)
-{
-       const struct ccc_state *ccc = a;
-       uint16_t handle = PTR_TO_UINT(b);
-
-       return ccc->handle == handle;
-}
-
-static struct ccc_state *find_ccc_state(struct device_state *dev_state,
-                                                               uint16_t handle)
-{
-       return queue_find(dev_state->ccc_states, ccc_state_match,
-                                                       UINT_TO_PTR(handle));
-}
-
-static struct device_state *device_state_create(bdaddr_t *bdaddr,
-                                                       uint8_t bdaddr_type)
-{
-       struct device_state *dev_state;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       char addr[18] = { 0 };
-
-       ba2str(bdaddr, addr);
-       DBG("create device_state for %s [%d]", addr, bdaddr_type);
-#endif
-
-       dev_state = new0(struct device_state, 1);
-       dev_state->ccc_states = queue_new();
-       bacpy(&dev_state->bdaddr, bdaddr);
-       dev_state->bdaddr_type = bdaddr_type;
-
-       return dev_state;
-}
-
 static struct device_state *get_device_state(struct btd_gatt_database *database,
-                                                       bdaddr_t *bdaddr,
-                                                       uint8_t bdaddr_type)
+                                               struct bt_att *att)
 {
        struct device_state *dev_state;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+
+       if (!get_dst_info(att, &bdaddr, &bdaddr_type))
+               return NULL;
 
        /*
         * Find and return a device state. If a matching state doesn't exist,
         * then create a new one.
         */
-       dev_state = find_device_state(database, bdaddr, bdaddr_type);
+       dev_state = find_device_state(database, &bdaddr, bdaddr_type);
        if (dev_state)
-               return dev_state;
+               goto done;
 
-       dev_state = device_state_create(bdaddr, bdaddr_type);
+       dev_state = device_state_create(database, &bdaddr, bdaddr_type);
 
        queue_push_tail(database->device_states, dev_state);
 
+done:
+       if (!dev_state->disc_id)
+               dev_state->disc_id = bt_att_register_disconnect(att,
+                                                       att_disconnected,
+                                                       dev_state, NULL);
+
        return dev_state;
 }
 
 static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
-                                                       bdaddr_t *bdaddr,
-                                                       uint8_t bdaddr_type,
-                                                       uint16_t handle)
+                                        struct bt_att *att, uint16_t handle)
 {
        struct device_state *dev_state;
        struct ccc_state *ccc;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       char addr[18] = { 0 };
-#endif
 
-       dev_state = get_device_state(database, bdaddr, bdaddr_type);
+       dev_state = get_device_state(database, att);
+       if (!dev_state)
+               return NULL;
 
        ccc = find_ccc_state(dev_state, handle);
        if (ccc)
                return ccc;
 
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       ba2str(bdaddr, addr);
-       DBG("create ccc_state of handle: 0x%04x for %s [%d]",
-                       handle, addr, bdaddr_type);
-#endif
-
        ccc = new0(struct ccc_state, 1);
        ccc->handle = handle;
        queue_push_tail(dev_state->ccc_states, ccc);
@@ -365,14 +459,6 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
        return ccc;
 }
 
-static void device_state_free(void *data)
-{
-       struct device_state *state = data;
-
-       queue_destroy(state->ccc_states, free);
-       free(state);
-}
-
 static void cancel_pending_read(void *data)
 {
        struct pending_op *op = data;
@@ -396,6 +482,9 @@ static void chrc_free(void *data)
 {
        struct external_chrc *chrc = data;
 
+       io_destroy(chrc->write_io);
+       io_destroy(chrc->notify_io);
+
        queue_destroy(chrc->pending_reads, cancel_pending_read);
        queue_destroy(chrc->pending_writes, cancel_pending_write);
 
@@ -500,6 +589,14 @@ static void app_free(void *data)
        free(app);
 }
 
+static void gatt_record_free(void *data)
+{
+       struct gatt_record *rec = data;
+
+       adapter_service_remove(rec->database->adapter, rec->handle);
+       free(rec);
+}
+
 static void gatt_database_free(void *data)
 {
        struct btd_gatt_database *database = data;
@@ -514,16 +611,10 @@ static void gatt_database_free(void *data)
                g_io_channel_unref(database->l2cap_io);
        }
 
-       if (database->gatt_handle)
-               adapter_service_remove(database->adapter,
-                                                       database->gatt_handle);
-
-       if (database->gap_handle)
-               adapter_service_remove(database->adapter, database->gap_handle);
-
        /* TODO: Persistently store CCC states before freeing them */
        gatt_db_unregister(database->db, database->db_id);
 
+       queue_destroy(database->records, gatt_record_free);
        queue_destroy(database->device_states, device_state_free);
        queue_destroy(database->apps, app_free);
        queue_destroy(database->profiles, profile_free);
@@ -750,21 +841,42 @@ static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
        return record;
 }
 
-static uint32_t database_add_record(struct btd_gatt_database *database,
-                                       uint16_t uuid,
-                                       struct gatt_db_attribute *attr,
-                                       const char *name)
+static void database_add_record(struct btd_gatt_database *database,
+                                       struct gatt_db_attribute *attr)
 {
+       struct gatt_record *rec;
        sdp_record_t *record;
        uint16_t start, end;
        uuid_t svc, gap_uuid;
+       bt_uuid_t uuid;
+       const char *name = NULL;
+       char uuidstr[MAX_LEN_UUID_STR];
+
+       gatt_db_attribute_get_service_uuid(attr, &uuid);
+
+       switch (uuid.type) {
+       case BT_UUID16:
+               name = bt_uuid16_to_str(uuid.value.u16);
+               sdp_uuid16_create(&svc, uuid.value.u16);
+               break;
+       case BT_UUID32:
+               name = bt_uuid32_to_str(uuid.value.u32);
+               sdp_uuid32_create(&svc, uuid.value.u32);
+               break;
+       case BT_UUID128:
+               bt_uuid_to_string(&uuid, uuidstr, sizeof(uuidstr));
+               name = bt_uuidstr_to_str(uuidstr);
+               sdp_uuid128_create(&svc, (void *) &uuid.value.u128);
+               break;
+       case BT_UUID_UNSPEC:
+               return;
+       }
 
-       sdp_uuid16_create(&svc, uuid);
        gatt_db_attribute_get_service_handles(attr, &start, &end);
 
        record = record_new(&svc, start, end);
        if (!record)
-               return 0;
+               return;
 
        if (name != NULL)
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -782,11 +894,16 @@ static uint32_t database_add_record(struct btd_gatt_database *database,
        }
 #endif
 
-       if (adapter_service_add(database->adapter, record) == 0)
-               return record->handle;
+       if (adapter_service_add(database->adapter, record) < 0) {
+               sdp_record_free(record);
+               return;
+       }
 
-       sdp_record_free(record);
-       return 0;
+       rec = new0(struct gatt_record, 1);
+       rec->database = database;
+       rec->handle = record->handle;
+       rec->attr = attr;
+       queue_push_tail(database->records, rec);
 }
 
 static void populate_gap_service(struct btd_gatt_database *database)
@@ -797,8 +914,6 @@ static void populate_gap_service(struct btd_gatt_database *database)
        /* Add the GAP service */
        bt_uuid16_create(&uuid, UUID_GAP);
        service = gatt_db_add_service(database->db, &uuid, true, 5);
-       database->gap_handle = database_add_record(database, UUID_GAP, service,
-                                               "Generic Access Profile");
 
        /*
         * Device Name characteristic.
@@ -830,34 +945,6 @@ static void populate_gap_service(struct btd_gatt_database *database)
        gatt_db_service_set_active(service, true);
 }
 
-static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
-{
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       return bt_att_get_remote_addr(att, dst, dst_type);
-#else
-       GIOChannel *io = NULL;
-       GError *gerr = NULL;
-
-       io = g_io_channel_unix_new(bt_att_get_fd(att));
-       if (!io)
-               return false;
-
-       bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
-                                               BT_IO_OPT_DEST_TYPE, dst_type,
-                                               BT_IO_OPT_INVALID);
-       if (gerr) {
-               error("gatt: bt_io_get: %s", gerr->message);
-               g_error_free(gerr);
-               g_io_channel_unref(io);
-               return false;
-       }
-
-       g_io_channel_unref(io);
-
-       return true;
-#endif
-}
-
 static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
                                        unsigned int id, uint16_t offset,
                                        uint8_t opcode, struct bt_att *att,
@@ -869,8 +956,6 @@ static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
        uint8_t ecode = 0;
        const uint8_t *value = NULL;
        size_t len = 0;
-       bdaddr_t bdaddr;
-       uint8_t bdaddr_type;
 
        handle = gatt_db_attribute_get_handle(attrib);
 
@@ -881,13 +966,12 @@ static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
                goto done;
        }
 
-       if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+       ccc = get_ccc_state(database, att, handle);
+       if (!ccc) {
                ecode = BT_ATT_ERROR_UNLIKELY;
                goto done;
        }
 
-       ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
-
        len = 2 - offset;
        value = len ? &ccc->value[offset] : NULL;
 
@@ -906,8 +990,11 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
        struct ccc_cb_data *ccc_cb;
        uint16_t handle;
        uint8_t ecode = 0;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        bdaddr_t bdaddr;
-       uint8_t bdaddr_type;
+        uint8_t bdaddr_type;
+#endif
 
        handle = gatt_db_attribute_get_handle(attrib);
 
@@ -923,13 +1010,19 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
                goto done;
        }
 
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+                ecode = BT_ATT_ERROR_UNLIKELY;
+                goto done;
+        }
+#endif
+
+       ccc = get_ccc_state(database, att, handle);
+       if (!ccc) {
                ecode = BT_ATT_ERROR_UNLIKELY;
                goto done;
        }
 
-       ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
-
        ccc_cb = queue_find(database->ccc_callbacks, ccc_cb_match_handle,
                        UINT_TO_PTR(gatt_db_attribute_get_handle(attrib)));
        if (!ccc_cb) {
@@ -942,7 +1035,8 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
                goto done;
 
        if (ccc_cb->callback)
-               ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+               ecode = ccc_cb->callback(att, get_le16(value),
+                                               ccc_cb->user_data);
 
        if (!ecode) {
                ccc->value[0] = value[0];
@@ -1011,9 +1105,6 @@ static void populate_gatt_service(struct btd_gatt_database *database)
        /* Add the GATT service */
        bt_uuid16_create(&uuid, UUID_GATT);
        service = gatt_db_add_service(database->db, &uuid, true, 4);
-       database->gatt_handle = database_add_record(database, UUID_GATT,
-                                               service,
-                                               "Generic Attribute Profile");
 
        bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
        database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
@@ -1038,6 +1129,7 @@ struct notify {
        const uint8_t *value;
        uint16_t len;
        bool indicate;
+       GDBusProxy *proxy;
 };
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -1098,7 +1190,14 @@ static void indicate_confirm_reply_cb(DBusMessage *message, void *user_data)
 
 static void conf_cb(void *user_data)
 {
+       GDBusProxy *proxy = user_data;
        DBG("GATT server received confirmation");
+
+       if (proxy != NULL)
+       {
+               g_dbus_proxy_method_call(proxy, "Confirm", NULL, NULL, NULL, NULL);
+       }
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        struct notify_indicate_cb *confirm = user_data;
 
@@ -1261,20 +1360,23 @@ static void send_notification_to_device(void *data, void *user_data)
        DBG("GATT server sending indication");
        bt_gatt_server_send_indication(server, notify->handle, notify->value,
                                                        notify->len, conf_cb,
-                                                       NULL, NULL);
+                                                       notify->proxy, NULL);
 
        return;
 
 remove:
        /* Remove device state if device no longer exists or is not paired */
-       if (queue_remove(notify->database->device_states, device_state))
+       if (queue_remove(notify->database->device_states, device_state)) {
+               queue_foreach(device_state->ccc_states, clear_ccc_state,
+                                               notify->database);
                device_state_free(device_state);
+       }
 }
 
 static void send_notification_to_devices(struct btd_gatt_database *database,
                                        uint16_t handle, const uint8_t *value,
                                        uint16_t len, uint16_t ccc_handle,
-                                       bool indicate)
+                                       bool indicate, GDBusProxy *proxy)
 {
        struct notify notify;
 
@@ -1286,6 +1388,7 @@ static void send_notification_to_devices(struct btd_gatt_database *database,
        notify.value = value;
        notify.len = len;
        notify.indicate = indicate;
+       notify.proxy = proxy;
 
        queue_foreach(database->device_states, send_notification_to_device,
                                                                &notify);
@@ -1316,7 +1419,7 @@ static void send_service_changed(struct btd_gatt_database *database,
        put_le16(end, value + 2);
 
        send_notification_to_devices(database, handle, value, sizeof(value),
-                                                       ccc_handle, true);
+                                                       ccc_handle, true, NULL);
 }
 
 static void gatt_db_service_added(struct gatt_db_attribute *attrib,
@@ -1326,6 +1429,8 @@ static void gatt_db_service_added(struct gatt_db_attribute *attrib,
 
        DBG("GATT Service added to local database");
 
+       database_add_record(database, attrib);
+
        send_service_changed(database, attrib);
 }
 
@@ -1348,13 +1453,26 @@ static void remove_device_ccc(void *data, void *user_data)
        queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
 }
 
+static bool match_gatt_record(const void *data, const void *user_data)
+{
+       const struct gatt_record *rec = data;
+       const struct gatt_db_attribute *attr = user_data;
+
+       return (rec->attr == attr);
+}
+
 static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
                                                                void *user_data)
 {
        struct btd_gatt_database *database = user_data;
+       struct gatt_record *rec;
 
        DBG("Local GATT service removed");
 
+       rec = queue_remove_if(database->records, match_gatt_record, attrib);
+       if (rec)
+               gatt_record_free(rec);
+
        send_service_changed(database, attrib);
 
        queue_foreach(database->device_states, remove_device_ccc, attrib);
@@ -1882,6 +2000,7 @@ static uint8_t dbus_error_to_att_ecode(const char *error_message)
 #else
 static uint8_t dbus_error_to_att_ecode(const char *error_name)
 {
+
        if (strcmp(error_name, "org.bluez.Error.Failed") == 0)
                return 0x80;  /* For now return this "application error" */
 
@@ -1904,7 +2023,6 @@ static uint8_t dbus_error_to_att_ecode(const char *error_name)
 static void read_reply_cb(DBusMessage *message, void *user_data)
 {
        struct pending_op *op = user_data;
-
        DBusError err;
        DBusMessageIter iter, array;
        uint8_t ecode = 0;
@@ -1969,17 +2087,11 @@ static void pending_op_free(void *data)
 
        free(op);
 }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-static struct pending_op *pending_read_new(struct btd_device *device,
-                                       struct queue *owner_queue,
-                                       struct gatt_db_attribute *attrib,
-                                       unsigned int id, uint16_t offset)
-#else
 static struct pending_op *pending_read_new(struct btd_device *device,
                                        struct queue *owner_queue,
                                        struct gatt_db_attribute *attrib,
-                                       unsigned int id)
-#endif
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t link_type)
 {
        struct pending_op *op;
 
@@ -1989,23 +2101,38 @@ static struct pending_op *pending_read_new(struct btd_device *device,
        op->device = device;
        op->attrib = attrib;
        op->id = id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        op->offset = offset;
-#endif
+       op->link_type = link_type;
        queue_push_tail(owner_queue, op);
 
        return op;
 }
 
-#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 static void append_options(DBusMessageIter *iter, void *user_data)
 {
        struct pending_op *op = user_data;
        const char *path = device_get_path(op->device);
+       const char *link;
+
+       switch (op->link_type) {
+       case BT_ATT_LINK_BREDR:
+               link = "BR/EDR";
+               break;
+       case BT_ATT_LINK_LE:
+               link = "LE";
+               break;
+       default:
+               link = NULL;
+               break;
+       }
 
        dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
+       if (op->offset)
+               dict_append_entry(iter, "offset", DBUS_TYPE_UINT16,
+                                                       &op->offset);
+       if (link)
+               dict_append_entry(iter, "link", DBUS_TYPE_STRING, &link);
 }
-#endif
 
 static void read_setup_cb(DBusMessageIter *iter, void *user_data)
 {
@@ -2035,27 +2162,18 @@ static void read_setup_cb(DBusMessageIter *iter, void *user_data)
 #endif
 }
 
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static struct pending_op *send_read(struct btd_device *device,
                                        struct gatt_db_attribute *attrib,
                                        GDBusProxy *proxy,
                                        struct queue *owner_queue,
                                        unsigned int id,
-                                       uint16_t offset)
-#else
-static struct pending_op *send_read(struct btd_device *device,
-                                       struct gatt_db_attribute *attrib,
-                                       GDBusProxy *proxy,
-                                       struct queue *owner_queue,
-                                       unsigned int id)
-#endif
+                                       uint16_t offset,
+                                       uint8_t link_type)
 {
        struct pending_op *op;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       op = pending_read_new(device, owner_queue, attrib, id, offset);
-#else
-       op = pending_read_new(device, owner_queue, attrib, id);
-#endif
+
+       op = pending_read_new(device, owner_queue, attrib, id, offset,
+                                                       link_type);
 
        if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb,
                                read_reply_cb, op, pending_op_free) == TRUE)
@@ -2092,13 +2210,12 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
 {
        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_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);
 
@@ -2115,7 +2232,6 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
 static void write_reply_cb(DBusMessage *message, void *user_data)
 {
        struct pending_op *op = user_data;
-
        DBusError err;
        DBusMessageIter iter;
        uint8_t ecode = 0;
@@ -2152,22 +2268,14 @@ static void write_reply_cb(DBusMessage *message, void *user_data)
 done:
        gatt_db_attribute_write_result(op->attrib, op->id, ecode);
 }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
 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,
                                        size_t len,
-                                       uint16_t offset)
-#else
-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,
-                                       size_t len)
-#endif
+                                       uint16_t offset, uint8_t link_type)
 {
        struct pending_op *op;
 
@@ -2180,40 +2288,181 @@ static struct pending_op *pending_write_new(struct btd_device *device,
        op->owner_queue = owner_queue;
        op->attrib = attrib;
        op->id = id;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        op->offset = offset;
-#endif
+       op->link_type = link_type;
        queue_push_tail(owner_queue, op);
 
        return op;
 }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
 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,
+                                        uint16_t offset, uint8_t link_type)
+{
+       struct pending_op *op;
+
+       op = pending_write_new(device, owner_queue, attrib, id, value, len,
+                       offset, link_type);
+
+       if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
+                               owner_queue ? write_reply_cb : NULL,
+                               op, pending_op_free) == TRUE)
+               return op;
+
+       pending_op_free(op);
+
+       return NULL;
+}
+
+static bool pipe_hup(struct io *io, void *user_data)
+{
+       struct external_chrc *chrc = user_data;
+
+       DBG("%p closed\n", io);
+
+       if (io == chrc->write_io)
+               chrc->write_io = NULL;
+       else
+               chrc->notify_io = NULL;
+
+       io_destroy(io);
+
+       return false;
+}
+
+static bool pipe_io_read(struct io *io, void *user_data)
+{
+       struct external_chrc *chrc = user_data;
+       uint8_t buf[512];
+       int fd = io_get_fd(io);
+       ssize_t bytes_read;
+
+       bytes_read = read(fd, buf, sizeof(buf));
+       if (bytes_read < 0)
+               return false;
+
+       send_notification_to_devices(chrc->service->app->database,
+                               gatt_db_attribute_get_handle(chrc->attrib),
+                               buf, bytes_read,
+                               gatt_db_attribute_get_handle(chrc->ccc),
+                               chrc->props & BT_GATT_CHRC_PROP_INDICATE,
+                               chrc->proxy);
+
+       return true;
+}
+
+static struct io *pipe_io_new(int fd, void *user_data)
+{
+       struct io *io;
+
+       io = io_new(fd);
+
+       io_set_close_on_destroy(io, true);
+
+       io_set_read_handler(io, pipe_io_read, user_data, NULL);
+
+       io_set_disconnect_handler(io, pipe_hup, user_data, NULL);
+
+       return io;
+}
+
+static int pipe_io_send(struct io *io, const void *data, size_t len)
+{
+       struct iovec iov;
+
+       iov.iov_base = (void *) data;
+       iov.iov_len = len;
+
+       return io_send(io, &iov, 1);
+}
+
+static void acquire_write_reply(DBusMessage *message, void *user_data)
+{
+       struct pending_op *op = user_data;
+       struct external_chrc *chrc;
+       DBusError err;
+       int fd;
+       uint16_t mtu;
+
+       chrc = gatt_db_attribute_get_user_data(op->attrib);
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, message) == TRUE) {
+               error("Failed to acquire write: %s\n", err.name);
+               dbus_error_free(&err);
+               goto retry;
+       }
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               error("Invalid AcquireWrite response\n");
+               goto retry;
+       }
+
+       DBG("AcquireWrite success: fd %d MTU %u\n", fd, mtu);
+
+       chrc->write_io = pipe_io_new(fd, chrc);
+
+       if (pipe_io_send(chrc->write_io, op->data.iov_base,
+                               op->data.iov_len) < 0)
+               goto retry;
+
+       gatt_db_attribute_write_result(op->attrib, op->id, 0);
+
+       return;
+
+retry:
+       send_write(op->device, op->attrib, chrc->proxy, NULL, op->id,
+                               op->data.iov_base, op->data.iov_len, 0,
+                               op->link_type);
+}
+
+static void acquire_write_setup(DBusMessageIter *iter, void *user_data)
+{
+       struct pending_op *op = user_data;
+       DBusMessageIter dict;
+       struct bt_gatt_server *server;
+       uint16_t mtu;
+
+       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);
+
+       server = btd_device_get_gatt_server(op->device);
+
+       mtu = bt_gatt_server_get_mtu(server);
+
+       dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &mtu);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static struct pending_op *acquire_write(struct external_chrc *chrc,
+                                       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,
-                                       uint16_t offset)
-#else
-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)
-#endif
+                                       uint8_t link_type)
 {
        struct pending_op *op;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       op = pending_write_new(device, owner_queue, attrib, id, value, len, offset);
-#else
-       op = pending_write_new(device, owner_queue, attrib, id, value, len);
-#endif
 
-       if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
-                                       owner_queue ? write_reply_cb : NULL,
-                                       op, pending_op_free) == TRUE)
+       op = pending_write_new(device, NULL, attrib, id, value, len, 0,
+                                                       link_type);
+
+       if (g_dbus_proxy_method_call(chrc->proxy, "AcquireWrite",
+                                       acquire_write_setup,
+                                       acquire_write_reply,
+                                       op, pending_op_free))
                return op;
 
        pending_op_free(op);
@@ -2221,9 +2470,64 @@ static struct pending_op *send_write(struct btd_device *device,
        return NULL;
 }
 
-static uint8_t ccc_write_cb(uint16_t value, void *user_data)
+static void acquire_notify_reply(DBusMessage *message, void *user_data)
 {
        struct external_chrc *chrc = user_data;
+       DBusError err;
+       int fd;
+       uint16_t mtu;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, message) == TRUE) {
+               error("Failed to acquire notify: %s\n", err.name);
+               dbus_error_free(&err);
+               goto retry;
+       }
+
+       if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &mtu,
+                                       DBUS_TYPE_INVALID) == false)) {
+               error("Invalid AcquirNotify response\n");
+               goto retry;
+       }
+
+       DBG("AcquireNotify success: fd %d MTU %u\n", fd, mtu);
+
+       chrc->notify_io = pipe_io_new(fd, chrc);
+
+       __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+
+       return;
+
+retry:
+       g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
+                                                       NULL, NULL);
+
+       __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+}
+
+static void acquire_notify_setup(DBusMessageIter *iter, void *user_data)
+{
+       DBusMessageIter dict;
+       struct external_chrc *chrc = user_data;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                       DBUS_TYPE_STRING_AS_STRING
+                                       DBUS_TYPE_VARIANT_AS_STRING
+                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                                       &dict);
+
+       dict_append_entry(&dict, "MTU", DBUS_TYPE_UINT16, &chrc->mtu);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static uint8_t ccc_write_cb(struct bt_att *att, uint16_t value, void *user_data)
+{
+       struct external_chrc *chrc = user_data;
+       DBusMessageIter iter;
 
        DBG("External CCC write received with value: 0x%04x", value);
 
@@ -2235,6 +2539,12 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
                if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
                        return 0;
 
+               if (chrc->notify_io) {
+                       io_destroy(chrc->notify_io);
+                       chrc->notify_io = NULL;
+                       return 0;
+               }
+
                /*
                 * Send request to stop notifying. This is best-effort
                 * operation, so simply ignore the return the value.
@@ -2255,12 +2565,27 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
                (value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
                return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
 
+       if (chrc->notify_io) {
+               __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+               return 0;
+       }
+
+       chrc->mtu = bt_att_get_mtu(att);
+
+       /* Make use of AcquireNotify if supported */
+       if (g_dbus_proxy_get_property(chrc->proxy, "NotifyAcquired", &iter)) {
+               if (g_dbus_proxy_method_call(chrc->proxy, "AcquireNotify",
+                                               acquire_notify_setup,
+                                               acquire_notify_reply,
+                                               chrc, NULL))
+                       return 0;
+       }
+
        /*
         * Always call StartNotify for an incoming enable and ignore the return
         * value for now.
         */
-       if (g_dbus_proxy_method_call(chrc->proxy,
-                                               "StartNotify", NULL, NULL,
+       if (g_dbus_proxy_method_call(chrc->proxy, "StartNotify", NULL, NULL,
                                                NULL, NULL) == FALSE)
                return BT_ATT_ERROR_UNLIKELY;
 
@@ -2381,7 +2706,7 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
                                gatt_db_attribute_get_handle(chrc->attrib),
                                value, len,
                                gatt_db_attribute_get_handle(chrc->ccc),
-                               chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+                               chrc->props & BT_GATT_CHRC_PROP_INDICATE, proxy);
 #endif
 }
 
@@ -2504,14 +2829,10 @@ static void desc_read_cb(struct gatt_db_attribute *attrib,
                error("Unable to find device object");
                goto fail;
        }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       if (send_read(device, attrib, desc->proxy, desc->pending_reads, id, offset))
-#else
-       if (send_read(device, attrib, desc->proxy, desc->pending_reads, id))
-#endif
+       if (send_read(device, attrib, desc->proxy, desc->pending_reads, id,
+                                       offset, bt_att_get_link_type(att)))
                return;
 
-
 fail:
        gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
                                                                NULL, 0);
@@ -2536,18 +2857,12 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
                error("Unable to find device object");
                goto fail;
        }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
-                                                       value, len, offset))
-#else
        if (send_write(device, attrib, desc->proxy, desc->pending_writes, id,
-                                                       value, len))
-#endif
+                               value, len, offset, bt_att_get_link_type(att)))
                return;
 
 fail:
-       gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
-                                                               NULL, 0);
+       gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY);
 }
 
 static bool database_add_desc(struct external_service *service,
@@ -2592,11 +2907,8 @@ static void chrc_read_cb(struct gatt_db_attribute *attrib,
                error("Unable to find device object");
                goto fail;
        }
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id, offset))
-#else
-       if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id))
-#endif
+       if (send_read(device, attrib, chrc->proxy, chrc->pending_reads, id,
+                                       offset, bt_att_get_link_type(att)))
                return;
 
 fail:
@@ -2613,6 +2925,7 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
        struct external_chrc *chrc = user_data;
        struct btd_device *device;
        struct queue *queue;
+       DBusMessageIter iter;
 
        if (chrc->attrib != attrib) {
                error("Write callback called with incorrect attribute");
@@ -2625,20 +2938,32 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
                goto fail;
        }
 
+       if (chrc->write_io) {
+               if (pipe_io_send(chrc->write_io, value, len) < 0) {
+                       error("Unable to write: %s", strerror(errno));
+                       goto fail;
+               }
+
+               gatt_db_attribute_write_result(attrib, id, 0);
+               return;
+       }
+
+       if (g_dbus_proxy_get_property(chrc->proxy, "WriteAcquired", &iter)) {
+               if (acquire_write(chrc, device, attrib, id, value, len,
+                                               bt_att_get_link_type(att)))
+                       return;
+       }
+
        if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
                queue = chrc->pending_writes;
        else
                queue = NULL;
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       if (send_write(device, attrib, chrc->proxy, queue, id, value, len, offset))
-#else
-       if (send_write(device, attrib, chrc->proxy, queue, id, value, len))
-#endif
+       if (send_write(device, attrib, chrc->proxy, queue, id, value, len,
+                                       offset, bt_att_get_link_type(att)))
                return;
 
 fail:
-       gatt_db_attribute_read_result(attrib, id, BT_ATT_ERROR_UNLIKELY,
-                                                               NULL, 0);
+       gatt_db_attribute_write_result(attrib, id, BT_ATT_ERROR_UNLIKELY);
 }
 
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
@@ -2820,6 +3145,15 @@ static void profile_device_remove(struct btd_service *service)
        DBG("%s removed", p->name);
 }
 
+static int profile_device_accept(struct btd_service *service)
+{
+       struct btd_profile *p = btd_service_get_profile(service);
+
+       DBG("%s accept", p->name);
+
+       return 0;
+}
+
 static int profile_add(struct external_profile *profile, const char *uuid)
 {
        struct btd_profile *p;
@@ -2843,6 +3177,7 @@ static int profile_add(struct external_profile *profile, const char *uuid)
 
        p->device_probe = profile_device_probe;
        p->device_remove = profile_device_remove;
+       p->accept = profile_device_accept;
        p->auto_connect = true;
        p->external = true;
 
@@ -3180,6 +3515,7 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
        database = new0(struct btd_gatt_database, 1);
        database->adapter = btd_adapter_ref(adapter);
        database->db = gatt_db_new();
+       database->records = queue_new();
        database->device_states = queue_new();
        database->apps = queue_new();
        database->profiles = queue_new();
index 6449879..6c6ffab 100755 (executable)
@@ -29,10 +29,15 @@ typedef enum {
        BT_MODE_LE,
 } bt_mode_t;
 
+typedef enum {
+       BT_GATT_CACHE_ALWAYS,
+       BT_GATT_CACHE_YES,
+       BT_GATT_CACHE_NO,
+} bt_gatt_cache_t;
+
 struct main_opts {
        char            *name;
        uint32_t        class;
-       uint16_t        autoto;
        uint32_t        pairto;
        uint32_t        discovto;
        uint8_t         privacy;
@@ -51,6 +56,7 @@ struct main_opts {
        uint16_t        did_version;
 
        bt_mode_t       mode;
+       bt_gatt_cache_t gatt_cache;
 };
 
 extern struct main_opts main_opts;
index 44110c5..5f64d52 100755 (executable)
@@ -69,6 +69,7 @@
 
 struct main_opts main_opts;
 static GKeyFile *main_conf;
+static char *main_conf_file_path;
 
 static enum {
        MPS_OFF,
@@ -76,25 +77,49 @@ static enum {
        MPS_MULTIPLE,
 } mps = MPS_OFF;
 
-static const char * const supported_options[] = {
+static const char *supported_options[] = {
        "Name",
        "Class",
        "DiscoverableTimeout",
-       "AlwaysPairable",
        "PairableTimeout",
-       "AutoConnectTimeout",
        "DeviceID",
        "ReverseServiceDiscovery",
        "NameResolving",
        "DebugKeys",
        "ControllerMode",
        "MultiProfile",
+       "FastConnectable",
        "Privacy",
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        "EnableLEPrivacy",
 #endif
+       NULL
 };
 
+static const char *policy_options[] = {
+       "ReconnectUUIDs",
+       "ReconnectAttempts",
+       "ReconnectIntervals",
+       "AutoEnable",
+       NULL
+};
+
+static const char *gatt_options[] = {
+       "Cache",
+       NULL
+};
+
+static const struct group_table {
+       const char *name;
+       const char **options;
+} valid_groups[] = {
+       { "General",    supported_options },
+       { "Policy",     policy_options },
+       { "GATT",       gatt_options },
+       { }
+};
+
+
 GKeyFile *btd_get_main_conf(void)
 {
        return main_conf;
@@ -152,53 +177,78 @@ done:
        main_opts.did_version = version;
 }
 
-static void check_config(GKeyFile *config)
+static bt_gatt_cache_t parse_gatt_cache(const char *cache)
+{
+       if (!strcmp(cache, "always")) {
+               return BT_GATT_CACHE_ALWAYS;
+       } else if (!strcmp(cache, "yes")) {
+               return BT_GATT_CACHE_YES;
+       } else if (!strcmp(cache, "no")) {
+               return BT_GATT_CACHE_NO;
+       } else {
+               DBG("Invalid value for KeepCache=%s", cache);
+               return BT_GATT_CACHE_ALWAYS;
+       }
+}
+
+static void check_options(GKeyFile *config, const char *group,
+                                               const char **options)
 {
-       const char *valid_groups[] = { "General", "Policy", NULL };
        char **keys;
        int i;
 
-       if (!config)
-               return;
-
-       keys = g_key_file_get_groups(config, NULL);
+       keys = g_key_file_get_keys(config, group, NULL, NULL);
 
        for (i = 0; keys != NULL && keys[i] != NULL; i++) {
-               const char **group;
-               bool match = false;
+               bool found;
+               unsigned int j;
 
-               for (group = valid_groups; *group; group++) {
-                       if (g_str_equal(keys[i], *group)) {
-                               match = true;
+               found = false;
+               for (j = 0; options != NULL && options[j] != NULL; j++) {
+                       if (g_str_equal(keys[i], options[j])) {
+                               found = true;
                                break;
                        }
                }
 
-               if (!match)
-                       warn("Unknown group %s in main.conf", keys[i]);
+               if (!found)
+                       warn("Unknown key %s for group %s in %s",
+                                       keys[i], group, main_conf_file_path);
        }
 
        g_strfreev(keys);
+}
 
-       keys = g_key_file_get_keys(config, "General", NULL, NULL);
+static void check_config(GKeyFile *config)
+{
+       char **keys;
+       int i;
+       const struct group_table *group;
+
+       if (!config)
+               return;
+
+       keys = g_key_file_get_groups(config, NULL);
 
        for (i = 0; keys != NULL && keys[i] != NULL; i++) {
-               bool found;
-               unsigned int j;
+               bool match = false;
 
-               found = false;
-               for (j = 0; j < G_N_ELEMENTS(supported_options); j++) {
-                       if (g_str_equal(keys[i], supported_options[j])) {
-                               found = true;
+               for (group = valid_groups; group && group->name ; group++) {
+                       if (g_str_equal(keys[i], group->name)) {
+                               match = true;
                                break;
                        }
                }
 
-               if (!found)
-                       warn("Unknown key %s in main.conf", keys[i]);
+               if (!match)
+                       warn("Unknown group %s in %s", keys[i],
+                                               main_conf_file_path);
        }
 
        g_strfreev(keys);
+
+       for (group = valid_groups; group && group->name; group++)
+               check_options(config, group->name, group->options);
 }
 
 static int get_mode(const char *str)
@@ -227,7 +277,7 @@ static void parse_config(GKeyFile *config)
 
        check_config(config);
 
-       DBG("parsing main.conf");
+       DBG("parsing %s", main_conf_file_path);
 
        val = g_key_file_get_integer(config, "General",
                                                "DiscoverableTimeout", &err);
@@ -249,16 +299,6 @@ static void parse_config(GKeyFile *config)
                main_opts.pairto = val;
        }
 
-       val = g_key_file_get_integer(config, "General", "AutoConnectTimeout",
-                                                                       &err);
-       if (err) {
-               DBG("%s", err->message);
-               g_clear_error(&err);
-       } else {
-               DBG("auto_to=%d", val);
-               main_opts.autoto = val;
-       }
-
        str = g_key_file_get_string(config, "General", "Privacy", &err);
        if (err) {
                DBG("%s", err->message);
@@ -368,6 +408,16 @@ static void parse_config(GKeyFile *config)
        else
                main_opts.le_privacy = boolean;
 #endif
+       str = g_key_file_get_string(config, "GATT", "Cache", &err);
+       if (err) {
+               g_clear_error(&err);
+               main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS;
+               return;
+       }
+
+       main_opts.gatt_cache = parse_gatt_cache(str);
+
+       g_free(str);
 }
 
 static void init_defaults(void)
@@ -505,6 +555,7 @@ static guint setup_signalfd(void)
 static char *option_debug = NULL;
 static char *option_plugin = NULL;
 static char *option_noplugin = NULL;
+static char *option_configfile = NULL;
 static gboolean option_compat = FALSE;
 static gboolean option_detach = TRUE;
 static gboolean option_version = FALSE;
@@ -520,6 +571,9 @@ static void free_options(void)
 
        g_free(option_noplugin);
        option_noplugin = NULL;
+
+       g_free(option_configfile);
+       option_configfile = NULL;
 }
 
 static void disconnect_dbus(void)
@@ -592,6 +646,8 @@ static GOptionEntry options[] = {
                                "Specify plugins to load", "NAME,..," },
        { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
                                "Specify plugins not to load", "NAME,..." },
+       { "configfile", 'f', 0, G_OPTION_ARG_STRING, &option_configfile,
+                       "Specify an explicit path to the config file", "FILE"},
        { "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat,
                                "Provide deprecated command line interfaces" },
        { "experimental", 'E', 0, G_OPTION_ARG_NONE, &option_experimental,
@@ -651,7 +707,12 @@ int main(int argc, char *argv[])
 
        sd_notify(0, "STATUS=Starting up");
 
-       main_conf = load_config(CONFIGDIR "/main.conf");
+       if (option_configfile)
+               main_conf_file_path = option_configfile;
+       else
+               main_conf_file_path = CONFIGDIR "/main.conf";
+
+       main_conf = load_config(main_conf_file_path);
 
        parse_config(main_conf);
 
index d9cd9e0..ba17405 100755 (executable)
@@ -1,6 +1,6 @@
 [General]
 
-# Default adaper name
+# Default adapter name
 # Defaults to 'BlueZ X.YZ'
 #Name = BlueZ
 
 # Defaults to "off"
 # Privacy = off
 
-#[Policy]
+[GATT]
+# GATT attribute cache.
+# Possible values:
+# always: Always cache attributes even for devices not paired, this is
+# recommended as it is best for interoperability, with more consistent
+# reconnection times and enables proper tracking of notifications for all
+# devices.
+# yes: Only cache attributes of paired devices.
+# no: Never cache attributes
+# Default: always
+#Cache = always
+
+[Policy]
 #
 # The ReconnectUUIDs defines the set of remote services that should try
 # to be reconnected to in case of a link loss (link supervision
diff --git a/src/oob.c b/src/oob.c
deleted file mode 100755 (executable)
index 708467b..0000000
--- a/src/oob.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011  ST-Ericsson SA
- *
- *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include "adapter.h"
-#include "oob.h"
-
-static oob_read_cb_t local_oob_read_cb = NULL;
-
-void oob_register_cb(oob_read_cb_t cb)
-{
-       local_oob_read_cb = cb;
-}
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
-                                                       uint8_t *randomizer, void *user_data)
-#else
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
-                                                       uint8_t *randomizer)
-#endif
-{
-       if (local_oob_read_cb)
-               local_oob_read_cb(adapter, hash, randomizer);
-}
diff --git a/src/oob.h b/src/oob.h
deleted file mode 100755 (executable)
index d720315..0000000
--- a/src/oob.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *
- *  BlueZ - Bluetooth protocol stack for Linux
- *
- *  Copyright (C) 2011  ST-Ericsson SA
- *
- *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
- *
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
-                                                       uint8_t *randomizer);
-
-void oob_register_cb(oob_read_cb_t cb);
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
-                                                       uint8_t *randomizer, void *user_data);
-#else
-void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
-                                                       uint8_t *randomizer);
-#endif
index 28a4db7..1dcb579 100755 (executable)
@@ -327,7 +327,6 @@ struct btd_profile *btd_service_get_profile(const struct btd_service *service)
 
 void btd_service_set_user_data(struct btd_service *service, void *user_data)
 {
-       btd_assert(service->state == BTD_SERVICE_STATE_UNAVAILABLE);
        service->user_data = user_data;
 }
 
index 1bf013d..15a6bdc 100755 (executable)
@@ -31,6 +31,8 @@
 
 struct bt_ad {
        int ref_count;
+       char *name;
+       uint16_t appearance;
        struct queue *service_uuids;
        struct queue *manufacturer_data;
        struct queue *solicit_uuids;
@@ -46,6 +48,7 @@ struct bt_ad *bt_ad_new(void)
        ad->manufacturer_data = queue_new();
        ad->solicit_uuids = queue_new();
        ad->service_data = queue_new();
+       ad->appearance = UINT16_MAX;
 
        return bt_ad_ref(ad);
 }
@@ -107,6 +110,8 @@ void bt_ad_unref(struct bt_ad *ad)
 
        queue_destroy(ad->service_data, uuid_destroy);
 
+       free(ad->name);
+
        free(ad);
 }
 
@@ -183,6 +188,21 @@ static size_t uuid_data_length(struct queue *uuid_data)
        return length;
 }
 
+static size_t name_length(const char *name, size_t *pos)
+{
+       size_t len;
+
+       if (!name)
+               return 0;
+
+       len = 2 + strlen(name);
+
+       if (len > MAX_ADV_DATA_LEN - *pos)
+               len = MAX_ADV_DATA_LEN - *pos;
+
+       return len;
+}
+
 static size_t calculate_length(struct bt_ad *ad)
 {
        size_t length = 0;
@@ -195,6 +215,10 @@ static size_t calculate_length(struct bt_ad *ad)
 
        length += uuid_data_length(ad->service_data);
 
+       length += name_length(ad->name, &length);
+
+       length += ad->appearance != UINT16_MAX ? 4 : 0;
+
        return length;
 }
 
@@ -313,6 +337,39 @@ static void serialize_service_data(struct queue *service_data, uint8_t *buf,
        }
 }
 
+static void serialize_name(const char *name, uint8_t *buf, uint8_t *pos)
+{
+       int len;
+       uint8_t type = EIR_NAME_COMPLETE;
+
+       if (!name)
+               return;
+
+       len = strlen(name);
+       if (len > MAX_ADV_DATA_LEN - (*pos + 2)) {
+               type = EIR_NAME_SHORT;
+               len = MAX_ADV_DATA_LEN - (*pos + 2);
+       }
+
+       buf[(*pos)++] = len + 1;
+       buf[(*pos)++] = type;
+
+       memcpy(buf + *pos, name, len);
+       *pos += len;
+}
+
+static void serialize_appearance(uint16_t value, uint8_t *buf, uint8_t *pos)
+{
+       if (value == UINT16_MAX)
+               return;
+
+       buf[(*pos)++] = sizeof(value) + 1;
+       buf[(*pos)++] = EIR_GAP_APPEARANCE;
+
+       bt_put_le16(value, buf + (*pos));
+       *pos += 2;
+}
+
 uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length)
 {
        uint8_t *adv_data;
@@ -338,6 +395,10 @@ uint8_t *bt_ad_generate(struct bt_ad *ad, size_t *length)
 
        serialize_service_data(ad->service_data, adv_data, &pos);
 
+       serialize_name(ad->name, adv_data, &pos);
+
+       serialize_appearance(ad->appearance, adv_data, &pos);
+
        return adv_data;
 }
 
@@ -656,3 +717,42 @@ void bt_ad_clear_service_data(struct bt_ad *ad)
 
        queue_remove_all(ad->service_data, NULL, NULL, uuid_destroy);
 }
+
+bool bt_ad_add_name(struct bt_ad *ad, const char *name)
+{
+       if (!ad)
+               return false;
+
+       free(ad->name);
+
+       ad->name = strdup(name);
+
+       return true;
+}
+
+void bt_ad_clear_name(struct bt_ad *ad)
+{
+       if (!ad)
+               return;
+
+       free(ad->name);
+       ad->name = NULL;
+}
+
+bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance)
+{
+       if (!ad)
+               return false;
+
+       ad->appearance = appearance;
+
+       return true;
+}
+
+void bt_ad_clear_appearance(struct bt_ad *ad)
+{
+       if (!ad)
+               return;
+
+       ad->appearance = UINT16_MAX;
+}
index 709563d..f0e3b81 100755 (executable)
@@ -88,3 +88,11 @@ void bt_ad_foreach_service_data(struct bt_ad *ad, bt_ad_func_t func,
 bool bt_ad_remove_service_data(struct bt_ad *ad, bt_uuid_t *uuid);
 
 void bt_ad_clear_service_data(struct bt_ad *ad);
+
+bool bt_ad_add_name(struct bt_ad *ad, const char *name);
+
+void bt_ad_clear_name(struct bt_ad *ad);
+
+bool bt_ad_add_appearance(struct bt_ad *ad, uint16_t appearance);
+
+void bt_ad_clear_appearance(struct bt_ad *ad);
index bb6a437..7818e9e 100755 (executable)
@@ -154,6 +154,9 @@ static enum att_op_type get_op_type(uint8_t opcode)
                        return att_opcode_type_table[i].type;
        }
 
+       if (opcode & ATT_OP_CMD_MASK)
+               return ATT_OP_TYPE_CMD;
+
        return ATT_OP_TYPE_UNKNOWN;
 }
 
@@ -506,7 +509,7 @@ static bool can_write_data(struct io *io, void *user_data)
                /* Set in_req to false to indicate that no request is pending */
                att->in_req = false;
 
-               /* Fall through to the next case */
+               /* fall through */
 #ifndef TIZEN_FEATURE_BLUEZ_MODIFY
        case ATT_OP_TYPE_CMD:
 #endif
@@ -665,17 +668,16 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
        if (!change_security(att, rsp->ecode))
                return false;
 
-       util_debug(att->debug_callback, att->debug_data,
-                                               "Retrying operation %p", op);
-
-       att->pending_req = NULL;
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+       /* Remove timeout_id if outstanding */
        if (op->timeout_id) {
                timeout_remove(op->timeout_id);
                op->timeout_id = 0;
        }
-#endif
+
+       util_debug(att->debug_callback, att->debug_data,
+                                               "Retrying operation %p", op);
+
+       att->pending_req = NULL;
 
        /* Push operation back to request queue */
        return queue_push_head(att->req_queue, op);
@@ -870,10 +872,10 @@ static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
        }
 
        /*
-        * If this was a request and no handler was registered for it, respond
-        * with "Not Supported"
+        * If this was not a command and no handler was registered for it,
+        * respond with "Not Supported"
         */
-       if (!found && get_op_type(opcode) == ATT_OP_TYPE_REQ)
+       if (!found && get_op_type(opcode) != ATT_OP_TYPE_CMD)
                respond_not_supported(att, opcode);
 
        bt_att_unref(att);
@@ -930,12 +932,12 @@ static bool can_read_data(struct io *io, void *user_data)
                }
 
                att->in_req = true;
-
-               /* Fall through to the next case */
+               /* fall through */
        case ATT_OP_TYPE_CMD:
        case ATT_OP_TYPE_NOT:
        case ATT_OP_TYPE_UNKNOWN:
        case ATT_OP_TYPE_IND:
+               /* fall through */
        default:
                /* For all other opcodes notify the upper layer of the PDU and
                 * let them act on it.
@@ -1233,6 +1235,17 @@ bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id)
        if (!att || !id)
                return false;
 
+       /* Check if disconnect is running */
+       if (!att->io) {
+               disconn = queue_find(att->disconn_list, match_disconn_id,
+                                                       UINT_TO_PTR(id));
+               if (!disconn)
+                       return false;
+
+               disconn->removed = true;
+               return true;
+       }
+
        disconn = queue_remove_if(att->disconn_list, match_disconn_id,
                                                        UINT_TO_PTR(id));
        if (!disconn)
diff --git a/src/shared/btp.c b/src/shared/btp.c
new file mode 100644 (file)
index 0000000..d981621
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2017  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 <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/btp.h"
+
+#define BTP_MTU 512
+
+struct btp_handler {
+       unsigned int id;
+       uint8_t service;
+       uint8_t opcode;
+
+       btp_cmd_func_t callback;
+       void *user_data;
+       btp_destroy_func_t destroy;
+};
+
+struct btp {
+       struct l_io *io;
+
+       struct l_queue *pending;
+       bool writer_active;
+       bool reader_active;
+
+       struct l_queue *handlers;
+       unsigned int next_handler;
+
+       uint8_t buf[BTP_MTU];
+
+       btp_disconnect_func_t disconnect_cb;
+       void *disconnect_cb_data;
+       btp_destroy_func_t disconnect_cb_data_destroy;
+};
+
+
+static struct l_io *btp_connect(const char *path)
+{
+       struct sockaddr_un addr;
+       struct l_io *io;
+       int sk;
+
+       sk = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
+       if (sk < 0)
+               return NULL;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return NULL;
+       }
+
+       io = l_io_new(sk);
+       if (!io) {
+               close(sk);
+               return NULL;
+       }
+
+       l_io_set_close_on_destroy(io, true);
+       return io;
+}
+
+static void disconnect_handler(struct l_io *io, void *user_data)
+{
+       struct btp *btp = user_data;
+
+       btp->disconnect_cb(btp, btp->disconnect_cb_data);
+}
+
+static void disconnect_handler_destroy(void *user_data)
+{
+       struct btp *btp = user_data;
+
+       if (btp->disconnect_cb_data_destroy)
+               btp->disconnect_cb_data_destroy(btp->disconnect_cb_data);
+}
+
+struct handler_match_data {
+       uint8_t service;
+       uint8_t opcode;
+};
+
+static bool handler_match(const void *a, const void *b)
+{
+       const struct btp_handler *handler = a;
+       const struct handler_match_data *match = b;
+
+       return (handler->service == match->service) &&
+                                       (handler->opcode == match->opcode);
+}
+
+static bool can_read_data(struct l_io *io, void *user_data)
+{
+       struct handler_match_data match;
+       struct btp *btp = user_data;
+       struct btp_handler *handler;
+       struct btp_hdr *hdr;
+       ssize_t bytes_read;
+       uint16_t data_len;
+
+       bytes_read = read(l_io_get_fd(btp->io), btp->buf, sizeof(btp->buf));
+       if (bytes_read < 0)
+               return false;
+
+       if ((size_t) bytes_read < sizeof(*hdr))
+               return false;
+
+       hdr = (void *)btp->buf;
+
+       data_len = L_LE16_TO_CPU(hdr->data_len);
+
+       if ((size_t) bytes_read < sizeof(*hdr) + data_len)
+               return false;
+
+       match.service = hdr->service;
+       match.opcode = hdr->opcode;
+
+       handler = l_queue_find(btp->handlers, handler_match, &match);
+       if (handler) {
+               handler->callback(hdr->index, hdr->data, data_len,
+                                                       handler->user_data);
+               return false;
+       }
+
+       /* keep reader active if we sent error reply */
+       btp_send_error(btp, match.service, hdr->index, BTP_ERROR_UNKNOWN_CMD);
+       return true;
+}
+
+static void read_watch_destroy(void *user_data)
+{
+       struct btp *btp = user_data;
+
+       btp->reader_active = false;
+}
+
+static void wakeup_reader(struct btp *btp)
+{
+       if (btp->reader_active)
+               return;
+
+       btp->reader_active = l_io_set_read_handler(btp->io, can_read_data, btp,
+                                                       read_watch_destroy);
+}
+
+struct btp *btp_new(const char *path)
+{
+       struct btp *btp;
+       struct l_io *io;
+
+       io = btp_connect(path);
+       if (!io)
+               return NULL;
+
+       btp = l_new(struct btp, 1);
+       btp->pending = l_queue_new();
+       btp->handlers = l_queue_new();
+       btp->io = io;
+       btp->next_handler = 1;
+
+       wakeup_reader(btp);
+
+       return btp;
+}
+
+struct pending_message {
+       size_t len;
+       void *data;
+       bool wakeup_read;
+};
+
+static void destroy_message(struct pending_message *msg)
+{
+       l_free(msg->data);
+       l_free(msg);
+}
+
+void btp_cleanup(struct btp *btp)
+{
+       if (!btp)
+               return;
+
+       l_io_destroy(btp->io);
+       l_queue_destroy(btp->pending, (l_queue_destroy_func_t)destroy_message);
+       l_queue_destroy(btp->handlers, (l_queue_destroy_func_t)l_free);
+       l_free(btp);
+}
+
+bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback,
+                               void *user_data, btp_destroy_func_t destroy)
+{
+       if (callback) {
+               if (!l_io_set_disconnect_handler(btp->io, disconnect_handler,
+                                       btp, disconnect_handler_destroy))
+                       return false;
+       } else {
+               if (!l_io_set_disconnect_handler(btp->io, NULL, NULL, NULL))
+                       return false;
+       }
+
+       btp->disconnect_cb = callback;
+       btp->disconnect_cb_data = user_data;
+       btp->disconnect_cb_data_destroy = destroy;
+
+       return true;
+}
+
+static bool can_write_data(struct l_io *io, void *user_data)
+{
+       struct btp *btp = user_data;
+       struct pending_message *msg;
+
+       msg = l_queue_pop_head(btp->pending);
+       if (!msg)
+               return false;
+
+       if (msg->wakeup_read)
+               wakeup_reader(btp);
+
+       if (write(l_io_get_fd(btp->io), msg->data, msg->len) < 0) {
+               l_error("Failed to send BTP message");
+               destroy_message(msg);
+               return false;
+       }
+
+       destroy_message(msg);
+
+       return !l_queue_isempty(btp->pending);
+}
+
+static void write_watch_destroy(void *user_data)
+{
+       struct btp *btp = user_data;
+
+       btp->writer_active = false;
+}
+
+static void wakeup_writer(struct btp *btp)
+{
+       if (l_queue_isempty(btp->pending))
+               return;
+
+       if (btp->writer_active)
+               return;
+
+       btp->writer_active = l_io_set_write_handler(btp->io, can_write_data,
+                                               btp, write_watch_destroy);
+}
+
+bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index,
+                                                               uint8_t status)
+{
+       struct btp_error rsp;
+
+       rsp.status = status;
+
+       return btp_send(btp, service, BTP_OP_ERROR, index, sizeof(rsp), &rsp);
+}
+
+bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index,
+                                       uint16_t length, const void *param)
+{
+       struct btp_hdr *hdr;
+       struct pending_message *msg;
+       size_t len;
+
+       len = sizeof(*hdr) + length;
+       hdr = l_malloc(len);
+       if (!hdr)
+               return NULL;
+
+       hdr->service = service;
+       hdr->opcode = opcode;
+       hdr->index = index;
+       hdr->data_len = L_CPU_TO_LE16(length);
+       if (length)
+               memcpy(hdr->data, param, length);
+
+       msg = l_new(struct pending_message, 1);
+       msg->len = len;
+       msg->data = hdr;
+       msg->wakeup_read = opcode < 0x80;
+
+       l_queue_push_tail(btp->pending, msg);
+       wakeup_writer(btp);
+
+       return true;
+}
+
+unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode,
+                               btp_cmd_func_t callback, void *user_data,
+                               btp_destroy_func_t destroy)
+{
+       struct btp_handler *handler;
+
+       handler = l_new(struct btp_handler, 1);
+
+       handler->id = btp->next_handler++;
+       handler->service = service;
+       handler->opcode = opcode;
+       handler->callback = callback;
+       handler->user_data = user_data;
+       handler->destroy = destroy;
+
+       l_queue_push_tail(btp->handlers, handler);
+
+       return handler->id;
+}
+
+static bool handler_match_by_id(const void *a, const void *b)
+{
+       const struct btp_handler *handler = a;
+       unsigned int id = L_PTR_TO_UINT(b);
+
+       return handler->id == id;
+}
+
+bool btp_unregister(struct btp *btp, unsigned int id)
+{
+       struct btp_handler *handler;
+
+       handler = l_queue_remove_if(btp->handlers, handler_match_by_id,
+                                                       L_UINT_TO_PTR(id));
+       if (!handler)
+               return false;
+
+       if (handler->destroy)
+               handler->destroy(handler->user_data);
+
+       l_free(handler);
+
+       return true;
+}
+
+static bool handler_remove_by_service(void *a, void *b)
+{
+       struct btp_handler *handler = a;
+       uint8_t service = L_PTR_TO_UINT(b);
+
+       if (handler->service != service)
+               return false;
+
+       if (handler->destroy)
+               handler->destroy(handler->user_data);
+
+       l_free(handler);
+       return true;
+}
+
+void btp_unregister_service(struct btp *btp, uint8_t service)
+{
+       l_queue_foreach_remove(btp->handlers, handler_remove_by_service,
+                                                       L_UINT_TO_PTR(service));
+}
diff --git a/src/shared/btp.h b/src/shared/btp.h
new file mode 100644 (file)
index 0000000..7b1f488
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2017  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 <stdbool.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define BTP_INDEX_NON_CONTROLLER 0xff
+
+#define BTP_ERROR_FAIL         0x01
+#define BTP_ERROR_UNKNOWN_CMD  0x02
+#define BTP_ERROR_NOT_READY    0x03
+#define BTP_ERROR_INVALID_INDEX        0x04
+
+#define BTP_CORE_SERVICE       0
+#define BTP_GAP_SERVICE                1
+#define BTP_GATT_SERVICE       2
+#define BTP_L2CAP_SERVICE      3
+#define BTP_MESH_NODE_SERVICE  4
+
+struct btp_hdr {
+       uint8_t service;
+       uint8_t opcode;
+       uint8_t index;
+       uint16_t data_len;
+       uint8_t data[0];
+} __packed;
+
+struct btp_error {
+       uint8_t status;
+} __packed;
+
+#define BTP_OP_ERROR                           0x00
+
+#define BTP_OP_CORE_READ_SUPPORTED_COMMANDS    0x01
+
+#define BTP_OP_CORE_READ_SUPPORTED_SERVICES    0x02
+
+#define BTP_OP_CORE_REGISTER                   0x03
+struct btp_core_register_cp {
+       uint8_t service_id;
+} __packed;
+
+#define BTP_OP_CORE_UNREGISTER                 0x04
+struct btp_core_unregister_cp {
+       uint8_t service_id;
+} __packed;
+
+#define BTP_EV_CORE_READY                      0x80
+
+#define BTP_OP_GAP_READ_SUPPORTED_COMMANDS     0x01
+
+#define BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST  0x02
+struct btp_gap_read_index_rp {
+       uint8_t num;
+       uint8_t indexes[0];
+} __packed;
+
+#define BTP_GAP_SETTING_POWERED                        0x00000001
+#define BTP_GAP_SETTING_CONNECTABLE            0x00000002
+#define BTP_GAP_SETTING_FAST_CONNECTABLE       0x00000004
+#define BTP_GAP_SETTING_DISCOVERABLE           0x00000008
+#define BTP_GAP_SETTING_BONDABLE               0x00000010
+#define BTP_GAP_SETTING_LL_SECURITY            0x00000020
+#define BTP_GAP_SETTING_SSP                    0x00000040
+#define BTP_GAP_SETTING_BREDR                  0x00000080
+#define BTP_GAP_SETTING_HS                     0x00000100
+#define BTP_GAP_SETTING_LE                     0x00000200
+#define BTP_GAP_SETTING_ADVERTISING            0x00000400
+#define BTP_GAP_SETTING_SC                     0x00000800
+#define BTP_GAP_SETTING_DEBUG_KEYS             0x00001000
+#define BTP_GAP_SETTING_PRIVACY                        0x00002000
+#define BTP_GAP_SETTING_CONTROLLER_CONF                0x00004000
+#define BTP_GAP_SETTING_STATIC_ADDRESS         0x00008000
+
+#define BTP_OP_GAP_READ_COTROLLER_INFO         0x03
+struct btp_gap_read_info_rp {
+       uint8_t address[6];
+       uint32_t supported_settings;
+       uint32_t current_settings;
+       uint8_t cod[3];
+       uint8_t name[249];
+       uint8_t short_name[11];
+} __packed;
+
+#define BTP_OP_GAP_RESET                       0x04
+struct btp_gap_reset_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_POWERED                 0x05
+struct btp_gap_set_powered_cp {
+       uint8_t powered;
+} __packed;
+
+struct btp_gap_set_powered_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_CONNECTABLE             0x06
+struct btp_gap_set_connectable_cp {
+       uint8_t connectable;
+} __packed;
+
+struct btp_gap_set_connectable_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_FAST_CONNECTABLE                0x07
+struct btp_gap_set_fast_connectable_cp {
+       uint8_t fast_connectable;
+} __packed;
+
+struct btp_gap_set_fast_connectable_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_DISCOVERABLE            0x08
+struct btp_gap_set_discoverable_cp {
+       uint8_t discoverable;
+} __packed;
+
+struct btp_gap_set_discoverable_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_SET_BONDABLE                        0x09
+struct btp_gap_set_bondable_cp {
+       uint8_t bondable;
+} __packed;
+
+struct btp_gap_set_bondable_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_START_ADVERTISING           0x0a
+struct btp_gap_start_adv_cp {
+       uint8_t adv_data_len;
+       uint8_t scan_rsp_len;
+       uint8_t data[0];
+} __packed;
+
+struct btp_gap_start_adv_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_OP_GAP_STOP_ADVERTISING            0x0b
+struct btp_gap_stop_adv_rp {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_GAP_DISCOVERY_FLAG_LE              0x01
+#define BTP_GAP_DISCOVERY_FLAG_BREDR           0x02
+#define BTP_GAP_DISCOVERY_FLAG_LIMITED         0x04
+#define BTP_GAP_DISCOVERY_FLAG_ACTIVE          0x08
+#define BTP_GAP_DISCOVERY_FLAG_OBSERVATION     0x10
+
+#define BTP_OP_GAP_START_DISCOVERY             0x0c
+struct btp_gap_start_discovery_cp {
+       uint8_t flags;
+} __packed;
+
+#define BTP_OP_GAP_STOP_DISCOVERY              0x0d
+
+#define BTP_GAP_ADDR_PUBLIC                    0x00
+#define BTP_GAP_ADDR_RANDOM                    0x01
+
+#define BTP_OP_GAP_CONNECT                     0x0e
+struct btp_gap_connect_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_DISCONNECT                  0x0f
+struct btp_gap_disconnect_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_GAP_IOCAPA_DISPLAY_ONLY            0x00
+#define BTP_GAP_IOCAPA_DISPLAY_YESNO           0x01
+#define BTP_GAP_IOCAPA_KEYBOARD_ONLY           0x02
+#define BTP_GAP_IOCAPA_NO_INPUT_NO_OUTPUT      0x03
+#define BTP_GAP_IOCAPA_KEYBOARD_DISPLAY                0x04
+
+#define BTP_OP_GAP_SET_IO_CAPA                 0x10
+struct btp_gap_set_io_capa_cp {
+       uint8_t capa;
+} __packed;
+
+#define BTP_OP_GAP_PAIR                                0x11
+struct btp_gap_pair_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_UNPAIR                      0x12
+struct btp_gap_unpair_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_OP_GAP_PASSKEY_ENTRY_RSP           0x13
+struct btp_gap_passkey_entry_rsp_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+       uint32_t passkey;
+} __packed;
+
+#define BTP_OP_GAP_PASSKEY_CONFIRM_RSP         0x14
+struct btp_gap_passkey_confirm_rsp_cp {
+       uint8_t address_type;
+       uint8_t address[6];
+       uint8_t match;
+} __packed;
+
+#define BTP_EV_GAP_NEW_SETTINGS                        0x80
+struct btp_new_settings_ev {
+       uint32_t current_settings;
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_RSSI      0x01
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_AD                0x02
+#define BTP_EV_GAP_DEVICE_FOUND_FLAG_SR                0x04
+
+#define BTP_EV_GAP_DEVICE_FOUND                        0x81
+struct btp_device_found_ev {
+       uint8_t address[6];
+       uint8_t address_type;
+       int8_t rssi;
+       uint8_t flags;
+       uint16_t eir_len;
+       uint8_t eir[0];
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_CONNECTED            0x82
+struct btp_gap_device_connected_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_DEVICE_DISCONNECTED         0x83
+struct btp_gap_device_disconnected_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_DISPLAY             0x84
+struct btp_gap_passkey_display_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+       uint32_t passkey;
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_REQUEST             0x85
+struct btp_gap_passkey_req_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+} __packed;
+
+#define BTP_EV_GAP_PASSKEY_CONFIRM             0x86
+struct btp_gap_passkey_confirm_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+       uint32_t passkey;
+} __packed;
+
+#define BTP_EV_GAP_IDENTITY_RESOLVED           0x87
+struct btp_gap_identity_resolved_ev {
+       uint8_t address_type;
+       uint8_t address[6];
+       uint8_t identity_address_type;
+       uint8_t identity_address[6];
+} __packed;
+
+struct btp;
+
+typedef void (*btp_destroy_func_t)(void *user_data);
+typedef void (*btp_disconnect_func_t)(struct btp *btp, void *user_data);
+typedef void (*btp_cmd_func_t)(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data);
+
+struct btp *btp_new(const char *path);
+void btp_cleanup(struct btp *btp);
+
+bool btp_set_disconnect_handler(struct btp *btp, btp_disconnect_func_t callback,
+                               void *user_data, btp_destroy_func_t destroy);
+
+bool btp_send_error(struct btp *btp, uint8_t service, uint8_t index,
+                                                               uint8_t status);
+bool btp_send(struct btp *btp, uint8_t service, uint8_t opcode, uint8_t index,
+                                       uint16_t length, const void *param);
+
+unsigned int btp_register(struct btp *btp, uint8_t service, uint8_t opcode,
+                               btp_cmd_func_t callback, void *user_data,
+                               btp_destroy_func_t destroy);
+bool btp_unregister(struct btp *btp, unsigned int id);
+void btp_unregister_service(struct btp *btp, uint8_t service);
index 5ffb655..4786b02 100644 (file)
 #define GATT_SVC_UUID  0x1801
 #define SVC_CHNGD_UUID 0x2a05
 
+struct ready_cb {
+       bt_gatt_client_callback_t callback;
+       bt_gatt_client_destroy_func_t destroy;
+       void *data;
+};
+
 struct bt_gatt_client {
        struct bt_att *att;
        int ref_count;
@@ -61,9 +67,7 @@ struct bt_gatt_client {
        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;
+       struct queue *ready_cbs;
 
        bt_gatt_client_service_changed_callback_t svc_chngd_callback;
        bt_gatt_client_destroy_func_t svc_chngd_destroy;
@@ -166,6 +170,9 @@ static struct request *request_create(struct bt_gatt_client *client)
 {
        struct request *req;
 
+       if (!client->att)
+               return NULL;
+
        req = new0(struct request, 1);
 
        if (client->next_request_id < 1)
@@ -372,6 +379,7 @@ static void discovery_op_free(struct discovery_op *op)
 static void discovery_op_complete(struct discovery_op *op, bool success,
                                                                uint8_t err)
 {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        const struct queue_entry *svc;
 
        /*
@@ -396,9 +404,9 @@ static void discovery_op_complete(struct discovery_op *op, bool success,
                gatt_db_remove_service(op->client->db, attr);
        }
 
+
        /* Reset remaining range */
        if (success) {
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                util_debug(op->client->debug_callback, op->client->debug_data,
                                "op->start : %u, op->end : %u, op->last : %u",
                                op->start, op->end, op->last);
@@ -411,21 +419,22 @@ static void discovery_op_complete(struct discovery_op *op, bool success,
                                gatt_db_clear_range(op->client->db,
                                                op->last, op->end);
                }
-#else
-               if (op->last != UINT16_MAX)
-                       gatt_db_clear_range(op->client->db, op->last + 1,
-                                                               UINT16_MAX);
-#endif
        } else {
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                util_debug(op->client->debug_callback, op->client->debug_data,
                                "Fail to discover service. Clear DB [%d]", err);
-#endif
                gatt_db_clear(op->client->db);
        }
 
        op->success = success;
        op->complete_func(op, success, err);
+#else
+       /* Reset remaining range */
+       if (op->last != UINT16_MAX)
+               gatt_db_clear_range(op->client->db, op->last + 1, UINT16_MAX);
+
+       op->success = success;
+       op->complete_func(op, success, err);
+#endif
 }
 
 static void discovery_load_services(struct gatt_db_attribute *attr,
@@ -462,6 +471,8 @@ static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
        op->end = end;
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        op->last = start;
+#else
+       op->last = gatt_db_isempty(client->db) ? 0 : UINT16_MAX;
 #endif
 
        /* Load existing services as pending */
@@ -494,7 +505,7 @@ static void discovery_op_unref(void *data)
        if (__sync_sub_and_fetch(&op->ref_count, 1))
                return;
 
-       if (!op->success)
+       if (!op->success && op->failure_func)
                op->failure_func(op);
 
        discovery_op_free(op);
@@ -752,13 +763,14 @@ static void ext_prop_read_cb(bool success, uint8_t att_ecode,
                return;
 
        if (!discover_descs(op, &discovering))
-               goto failed;
+                       goto failed;
 
        if (discovering)
                return;
 
        /* Done with the current service */
        gatt_db_service_set_active(op->cur_svc, true);
+
        goto done;
 
 failed:
@@ -1095,6 +1107,8 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode,
                                success = false;
                                goto done;
                        }
+                       /* Database has changed adjust last handle */
+                       op->last = end;
                }
 
                /* Update pending list */
@@ -1125,6 +1139,7 @@ next:
        util_debug(client->debug_callback, client->debug_data,
                                "Failed to start included services discovery");
        discovery_op_unref(op);
+       success = false;
 
 done:
        discovery_op_complete(op, success, att_ecode);
@@ -1150,15 +1165,14 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
                                        "Primary service discovery failed."
                                        " ATT ECODE: 0x%02x", att_ecode);
                /* Reset error in case of not found */
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-               if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
-#else
-               if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
-#endif
+               switch (att_ecode) {
+               case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND:
                        success = true;
                        att_ecode = 0;
+                       goto secondary;
+               default:
+                       goto done;
                }
-               goto secondary;
        }
 
        if (!result || !bt_gatt_iter_init(&iter, result)) {
@@ -1199,6 +1213,8 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
                                success = false;
                                goto done;
                        }
+                       /* Database has changed adjust last handle */
+                       op->last = end;
                }
 
                /* Update pending list */
@@ -1233,17 +1249,35 @@ done:
        discovery_op_complete(op, success, att_ecode);
 }
 
+static void ready_destroy(void *data)
+{
+       struct ready_cb *ready = data;
+
+       if (ready->destroy)
+               ready->destroy(ready->data);
+
+       free(ready);
+}
+
 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)
+       if (client->ready)
                return;
 
        bt_gatt_client_ref(client);
        client->ready = success;
-       client->ready_callback(success, att_ecode, client->ready_data);
+
+       for (entry = queue_get_entries(client->ready_cbs); entry;
+                                                       entry = entry->next) {
+               struct ready_cb *ready = entry->data;
+
+               ready->callback(success, att_ecode, ready->data);
+       }
+
+       queue_remove_all(client->ready_cbs, NULL, NULL, ready_destroy);
 
        /* Notify clones */
        for (entry = queue_get_entries(client->clones); entry;
@@ -1405,7 +1439,7 @@ static void enable_ccc_callback(uint8_t opcode, const void *pdu,
                                        uint16_t length, void *user_data)
 {
        struct notify_data *notify_data = user_data;
-       uint16_t att_ecode;
+       uint8_t att_ecode;
 
        assert(notify_data->chrc->ccc_write_id);
 
@@ -1787,18 +1821,6 @@ done:
 #endif
 }
 
-static void init_fail(struct discovery_op *op)
-{
-       struct bt_gatt_client *client = op->client;
-
-#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
-       util_debug(client->debug_callback, client->debug_data,
-                       "GATT client init is failed");
-#endif
-
-       gatt_db_clear(client->db);
-}
-
 static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
 {
        struct discovery_op *op;
@@ -1806,8 +1828,7 @@ static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
        if (client->in_init || client->ready)
                return false;
 
-       op = discovery_op_create(client, 0x0001, 0xffff, init_complete,
-                                                               init_fail);
+       op = discovery_op_create(client, 0x0001, 0xffff, init_complete, NULL);
        if (!op)
                return false;
 
@@ -1991,8 +2012,7 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
 
        queue_destroy(client->notify_list, notify_data_cleanup);
 
-       if (client->ready_destroy)
-               client->ready_destroy(client->ready_data);
+       queue_destroy(client->ready_cbs, ready_destroy);
 
        if (client->debug_destroy)
                client->debug_destroy(client->debug_data);
@@ -2066,6 +2086,7 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
                goto fail;
 
        client->clones = queue_new();
+       client->ready_cbs = queue_new();
        client->long_write_queue = queue_new();
        client->svc_chngd_queue = queue_new();
        client->notify_list = queue_new();
@@ -2166,22 +2187,37 @@ bool bt_gatt_client_is_ready(struct bt_gatt_client *client)
        return (client && client->ready);
 }
 
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
                                        bt_gatt_client_callback_t callback,
                                        void *user_data,
                                        bt_gatt_client_destroy_func_t destroy)
 {
+       struct ready_cb *ready;
+
        if (!client)
-               return false;
+               return 0;
 
-       if (client->ready_destroy)
-               client->ready_destroy(client->ready_data);
+       ready = new0(struct ready_cb, 1);
+       ready->callback = callback;
+       ready->destroy = destroy;
+       ready->data = user_data;
 
-       client->ready_callback = callback;
-       client->ready_destroy = destroy;
-       client->ready_data = user_data;
+       queue_push_tail(client->ready_cbs, ready);
 
-       return true;
+       return PTR_TO_UINT(ready);
+}
+
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+                                               unsigned int id)
+{
+       struct ready_cb *ready = UINT_TO_PTR(id);
+
+       if (queue_remove(client->ready_cbs, ready)) {
+               ready_destroy(ready);
+               return true;
+       }
+
+       return false;
 }
 
 bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
index 58931c4..d04b782 100755 (executable)
@@ -57,10 +57,12 @@ typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
                                                        void *user_data);
 
 bool bt_gatt_client_is_ready(struct bt_gatt_client *client);
-bool bt_gatt_client_set_ready_handler(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_ready_register(struct bt_gatt_client *client,
                                        bt_gatt_client_callback_t callback,
                                        void *user_data,
                                        bt_gatt_client_destroy_func_t destroy);
+bool bt_gatt_client_ready_unregister(struct bt_gatt_client *client,
+                                               unsigned int id);
 bool bt_gatt_client_set_service_changed(struct bt_gatt_client *client,
                        bt_gatt_client_service_changed_callback_t callback,
                        void *user_data,
index 5cc3b57..b99263c 100644 (file)
@@ -414,14 +414,7 @@ bool gatt_db_remove_service(struct gatt_db *db,
 
 bool gatt_db_clear(struct gatt_db *db)
 {
-       if (!db)
-               return false;
-
-       queue_remove_all(db->services, NULL, NULL, gatt_db_service_destroy);
-
-       db->next_handle = 0;
-
-       return true;
+       return gatt_db_clear_range(db, 1, UINT16_MAX);
 }
 
 static void gatt_db_service_get_handles(const struct gatt_db_service *service,
@@ -459,12 +452,23 @@ bool gatt_db_clear_range(struct gatt_db *db, uint16_t start_handle,
        if (!db || start_handle > end_handle)
                return false;
 
+       /* Check if it is a full clear */
+       if (start_handle == 1 && end_handle == UINT16_MAX) {
+               queue_remove_all(db->services, NULL, NULL,
+                                               gatt_db_service_destroy);
+               goto done;
+       }
+
        range.start = start_handle;
        range.end = end_handle;
 
        queue_remove_all(db->services, match_range, &range,
                                                gatt_db_service_destroy);
 
+done:
+       if (gatt_db_isempty(db))
+               db->next_handle = 0;
+
        return true;
 }
 
@@ -1095,10 +1099,15 @@ static void find_by_type(void *data, void *user_data)
                        continue;
 
                /* TODO: fix for read-callback based attributes */
-               if (search_data->value && memcmp(attribute->value,
-                                                       search_data->value,
-                                                       search_data->value_len))
-                       continue;
+               if (search_data->value) {
+                       if (search_data->value_len != attribute->value_len)
+                               continue;
+
+                       if (memcmp(attribute->value, search_data->value,
+                                       search_data->value_len)) {
+                               continue;
+                       }
+               }
 
                search_data->num_of_res++;
                search_data->func(attribute, search_data->user_data);
@@ -1916,3 +1925,11 @@ const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc)
        return NULL;
 }
 #endif
+
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib)
+{
+       if (!attrib)
+               return NULL;
+
+       return attrib->user_data;
+}
index 1c3785a..92a23e8 100644 (file)
@@ -277,3 +277,4 @@ void set_ccc_unicast_address(struct gatt_db_attribute *ccc,
 
 const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
 #endif
+void *gatt_db_attribute_get_user_data(struct gatt_db_attribute *attrib);
index f89ae01..640ac29 100755 (executable)
@@ -829,8 +829,7 @@ static void write_cb(uint8_t opcode, const void *pdu,
                                                        write_complete_cb, op))
                return;
 
-       if (op)
-               async_write_op_destroy(op);
+       async_write_op_destroy(op);
 
        ecode = BT_ATT_ERROR_UNLIKELY;
 
@@ -1559,6 +1558,14 @@ struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
        return bt_gatt_server_ref(server);
 }
 
+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server)
+{
+       if (!server || !server->att)
+               return 0;
+
+       return bt_att_get_mtu(server->att);
+}
+
 struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server)
 {
        if (!server)
index 9f66b55..4e84fa7 100755 (executable)
@@ -32,6 +32,7 @@ struct bt_gatt_server;
 
 struct bt_gatt_server *bt_gatt_server_new(struct gatt_db *db,
                                        struct bt_att *att, uint16_t mtu);
+uint16_t bt_gatt_server_get_mtu(struct bt_gatt_server *server);
 
 struct bt_gatt_server *bt_gatt_server_ref(struct bt_gatt_server *server);
 void bt_gatt_server_unref(struct bt_gatt_server *server);
index 9fff615..09c46a7 100755 (executable)
@@ -283,6 +283,8 @@ static void timeout_destroy(void *user_data)
 
        if (data->destroy)
                data->destroy(data->user_data);
+
+       free(data);
 }
 
 static void timeout_callback(int fd, uint32_t events, void *user_data)
@@ -311,7 +313,7 @@ static inline int timeout_set(int fd, unsigned int msec)
        itimer.it_interval.tv_sec = 0;
        itimer.it_interval.tv_nsec = 0;
        itimer.it_value.tv_sec = sec;
-       itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000;
+       itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000 * 1000;
 
        return timerfd_settime(fd, 0, &itimer, NULL);
 }
diff --git a/src/shared/shell.c b/src/shared/shell.c
new file mode 100644 (file)
index 0000000..6cdea1c
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <wordexp.h>
+#include <getopt.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/shell.h"
+
+#define CMD_LENGTH     48
+#define print_text(color, fmt, args...) \
+               printf(color fmt COLOR_OFF "\n", ## args)
+#define print_menu(cmd, args, desc) \
+               printf(COLOR_HIGHLIGHT "%s %-*s " COLOR_OFF "%s\n", \
+                       cmd, (int)(CMD_LENGTH - strlen(cmd)), args, desc)
+#define print_submenu(cmd, desc) \
+               printf(COLOR_BLUE "%s %-*s " COLOR_OFF "%s\n", \
+                       cmd, (int)(CMD_LENGTH - strlen(cmd)), "", desc)
+
+static GMainLoop *main_loop;
+
+static struct {
+       struct io *input;
+
+       bool saved_prompt;
+       bt_shell_prompt_input_func saved_func;
+       void *saved_user_data;
+
+       const struct bt_shell_menu *menu;
+       const struct bt_shell_menu *main;
+       struct queue *submenus;
+} data;
+
+static void shell_print_menu(void);
+
+static void cmd_version(int argc, char *argv[])
+{
+       bt_shell_printf("Version %s\n", VERSION);
+}
+
+static void cmd_quit(int argc, char *argv[])
+{
+       g_main_loop_quit(main_loop);
+}
+
+static void cmd_help(int argc, char *argv[])
+{
+       shell_print_menu();
+}
+
+static const struct bt_shell_menu *find_menu(const char *name)
+{
+       const struct queue_entry *entry;
+
+       for (entry = queue_get_entries(data.submenus); entry;
+                                               entry = entry->next) {
+               struct bt_shell_menu *menu = entry->data;
+
+               if (!strcmp(menu->name, name))
+                       return menu;
+       }
+
+       return NULL;
+}
+
+static char *menu_generator(const char *text, int state)
+{
+       static unsigned int index, len;
+       static struct queue_entry *entry;
+
+       if (!state) {
+               index = 0;
+               len = strlen(text);
+               entry = (void *) queue_get_entries(data.submenus);
+       }
+
+       for (; entry; entry = entry->next) {
+               struct bt_shell_menu *menu = entry->data;
+
+               index++;
+
+               if (!strncmp(menu->name, text, len)) {
+                       entry = entry->next;
+                       return strdup(menu->name);
+               }
+       }
+
+       return NULL;
+}
+
+static void cmd_menu(int argc, char *argv[])
+{
+       const struct bt_shell_menu *menu;
+
+       if (argc < 2 || !strlen(argv[1])) {
+               bt_shell_printf("Missing name argument\n");
+               return;
+       }
+
+       menu = find_menu(argv[1]);
+       if (!menu) {
+               bt_shell_printf("Unable find menu with name: %s\n", argv[1]);
+               return;
+       }
+
+       bt_shell_set_menu(menu);
+
+       shell_print_menu();
+}
+
+static bool cmd_menu_exists(const struct bt_shell_menu *menu)
+{
+       /* Skip menu command if not on main menu or if there are no
+        * submenus.
+        */
+       if (menu != data.main || queue_isempty(data.submenus))
+               return false;
+
+       return true;
+}
+
+static void cmd_back(int argc, char *argv[])
+{
+       if (data.menu == data.main) {
+               bt_shell_printf("Already on main menu\n");
+               return;
+       }
+
+       bt_shell_set_menu(data.main);
+
+       shell_print_menu();
+}
+
+static bool cmd_back_exists(const struct bt_shell_menu *menu)
+{
+       /* Skip back command if on main menu */
+       if (menu == data.main)
+               return false;
+
+       return true;
+}
+
+static const struct bt_shell_menu_entry default_menu[] = {
+       { "back",         NULL,       cmd_back, "Return to main menu", NULL,
+                                                       NULL, cmd_back_exists },
+       { "menu",         "<name>",   cmd_menu, "Select submenu",
+                                                       menu_generator, NULL,
+                                                       cmd_menu_exists},
+       { "version",      NULL,       cmd_version, "Display version" },
+       { "quit",         NULL,       cmd_quit, "Quit program" },
+       { "exit",         NULL,       cmd_quit, "Quit program" },
+       { "help",         NULL,       cmd_help,
+                                       "Display help about this program" },
+       { }
+};
+
+static void shell_print_menu(void)
+{
+       const struct bt_shell_menu_entry *entry;
+       const struct queue_entry *submenu;
+
+       if (!data.menu)
+               return;
+
+       print_text(COLOR_HIGHLIGHT, "Menu %s:", data.menu->name);
+       print_text(COLOR_HIGHLIGHT, "Available commands:");
+       print_text(COLOR_HIGHLIGHT, "-------------------");
+
+       if (data.menu == data.main) {
+               for (submenu = queue_get_entries(data.submenus); submenu;
+                                               submenu = submenu->next) {
+                       struct bt_shell_menu *menu = submenu->data;
+
+                       print_submenu(menu->name, menu->desc ? menu->desc :
+                                                               "Submenu");
+               }
+       }
+
+       for (entry = data.menu->entries; entry->cmd; entry++) {
+               print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
+       }
+
+       for (entry = default_menu; entry->cmd; entry++) {
+               if (entry->exists && !entry->exists(data.menu))
+                       continue;
+
+               print_menu(entry->cmd, entry->arg ? : "", entry->desc ? : "");
+       }
+}
+
+static int parse_args(char *arg, wordexp_t *w, char *del, int flags)
+{
+       char *str;
+
+       str = g_strdelimit(arg, del, '"');
+
+       if (wordexp(str, w, flags)) {
+               g_free(str);
+               return -EINVAL;
+       }
+
+       /* If argument ends with ,,, set we_offs bypass strict checks */
+       if (w->we_wordc && g_str_has_suffix(w->we_wordv[w->we_wordc -1], "..."))
+               w->we_offs = 1;
+
+       g_free(str);
+
+       return 0;
+}
+
+static int cmd_exec(const struct bt_shell_menu_entry *entry,
+                                       int argc, char *argv[])
+{
+       wordexp_t w;
+       size_t len;
+       char *man, *opt;
+       int flags = WRDE_NOCMD;
+
+       if (!entry->arg || entry->arg[0] == '\0') {
+               if (argc > 1) {
+                       print_text(COLOR_HIGHLIGHT, "Too many arguments");
+                       return -EINVAL;
+               }
+               goto exec;
+       }
+
+       /* Find last mandatory arguments */
+       man = strrchr(entry->arg, '>');
+       if (!man) {
+               opt = strdup(entry->arg);
+               goto optional;
+       }
+
+       len = man - entry->arg;
+       man = strndup(entry->arg, len + 1);
+
+       if (parse_args(man, &w, "<>", flags) < 0) {
+               print_text(COLOR_HIGHLIGHT,
+                               "Unable to parse mandatory command arguments");
+               return -EINVAL;
+       }
+
+       /* Check if there are enough arguments */
+       if ((unsigned) argc - 1 < w.we_wordc && !w.we_offs) {
+               print_text(COLOR_HIGHLIGHT, "Missing %s argument",
+                                               w.we_wordv[argc - 1]);
+               goto fail;
+       }
+
+       flags |= WRDE_APPEND;
+       opt = strdup(entry->arg + len + 1);
+
+optional:
+       if (parse_args(opt, &w, "[]", flags) < 0) {
+               print_text(COLOR_HIGHLIGHT,
+                               "Unable to parse optional command arguments");
+               return -EINVAL;
+       }
+
+       /* Check if there are too many arguments */
+       if ((unsigned) argc - 1 > w.we_wordc && !w.we_offs) {
+               print_text(COLOR_HIGHLIGHT, "Too many arguments: %d > %zu",
+                                       argc - 1, w.we_wordc);
+               goto fail;
+       }
+
+       wordfree(&w);
+
+exec:
+       if (entry->func)
+               entry->func(argc, argv);
+
+       return 0;
+
+fail:
+       wordfree(&w);
+       return -EINVAL;
+}
+
+static int menu_exec(const struct bt_shell_menu_entry *entry,
+                                       int argc, char *argv[])
+{
+       for (; entry->cmd; entry++) {
+               if (strcmp(argv[0], entry->cmd))
+                       continue;
+
+               /* Skip menu command if not on main menu */
+               if (data.menu != data.main && !strcmp(entry->cmd, "menu"))
+                       continue;
+
+               /* Skip back command if on main menu */
+               if (data.menu == data.main && !strcmp(entry->cmd, "back"))
+                       continue;
+
+               return cmd_exec(entry, argc, argv);
+       }
+
+       return -ENOENT;
+}
+
+static void shell_exec(int argc, char *argv[])
+{
+       if (!data.menu || !argv[0])
+               return;
+
+       if (menu_exec(default_menu, argc, argv) == -ENOENT) {
+               if (menu_exec(data.menu->entries, argc, argv) == -ENOENT)
+                       print_text(COLOR_HIGHLIGHT, "Invalid command");
+       }
+}
+
+void bt_shell_printf(const char *fmt, ...)
+{
+       va_list args;
+       bool save_input;
+       char *saved_line;
+       int saved_point;
+
+       save_input = !RL_ISSTATE(RL_STATE_DONE);
+
+       if (save_input) {
+               saved_point = rl_point;
+               saved_line = rl_copy_text(0, rl_end);
+               if (!data.saved_prompt) {
+                       rl_save_prompt();
+                       rl_replace_line("", 0);
+                       rl_redisplay();
+               }
+       }
+
+       va_start(args, fmt);
+       vprintf(fmt, args);
+       va_end(args);
+
+       if (save_input) {
+               if (!data.saved_prompt)
+                       rl_restore_prompt();
+               rl_replace_line(saved_line, 0);
+               rl_point = saved_point;
+               rl_forced_update_display();
+               free(saved_line);
+       }
+}
+
+void bt_shell_hexdump(const unsigned char *buf, size_t len)
+{
+       static const char hexdigits[] = "0123456789abcdef";
+       char str[68];
+       size_t i;
+
+       if (!len)
+               return;
+
+       str[0] = ' ';
+
+       for (i = 0; i < len; i++) {
+               str[((i % 16) * 3) + 1] = ' ';
+               str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
+               str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
+               str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
+
+               if ((i + 1) % 16 == 0) {
+                       str[49] = ' ';
+                       str[50] = ' ';
+                       str[67] = '\0';
+                       bt_shell_printf("%s\n", str);
+                       str[0] = ' ';
+               }
+       }
+
+       if (i % 16 > 0) {
+               size_t j;
+               for (j = (i % 16); j < 16; j++) {
+                       str[(j * 3) + 1] = ' ';
+                       str[(j * 3) + 2] = ' ';
+                       str[(j * 3) + 3] = ' ';
+                       str[j + 51] = ' ';
+               }
+               str[49] = ' ';
+               str[50] = ' ';
+               str[67] = '\0';
+               bt_shell_printf("%s\n", str);
+       }
+}
+
+void bt_shell_prompt_input(const char *label, const char *msg,
+                       bt_shell_prompt_input_func func, void *user_data)
+{
+       /* Normal use should not prompt for user input to the value a second
+        * time before it releases the prompt, but we take a safe action. */
+       if (data.saved_prompt)
+               return;
+
+       rl_save_prompt();
+       rl_message(COLOR_RED "[%s]" COLOR_OFF " %s ", label, msg);
+
+       data.saved_prompt = true;
+       data.saved_func = func;
+       data.saved_user_data = user_data;
+}
+
+int bt_shell_release_prompt(const char *input)
+{
+       bt_shell_prompt_input_func func;
+       void *user_data;
+
+       if (!data.saved_prompt)
+               return -1;
+
+       data.saved_prompt = false;
+
+       rl_restore_prompt();
+
+       func = data.saved_func;
+       user_data = data.saved_user_data;
+
+       data.saved_func = NULL;
+       data.saved_user_data = NULL;
+
+       func(input, user_data);
+
+       return 0;
+}
+
+static void rl_handler(char *input)
+{
+       wordexp_t w;
+
+       if (!input) {
+               rl_insert_text("quit");
+               rl_redisplay();
+               rl_crlf();
+               g_main_loop_quit(main_loop);
+               return;
+       }
+
+       if (!strlen(input))
+               goto done;
+
+       if (!bt_shell_release_prompt(input))
+               goto done;
+
+       if (history_search(input, -1))
+               add_history(input);
+
+       if (wordexp(input, &w, WRDE_NOCMD))
+               goto done;
+
+       if (w.we_wordc == 0) {
+               wordfree(&w);
+               goto done;
+       }
+
+       shell_exec(w.we_wordc, w.we_wordv);
+       wordfree(&w);
+done:
+       free(input);
+}
+
+static char *find_cmd(const char *text,
+                       const struct bt_shell_menu_entry *entry, int *index)
+{
+       const struct bt_shell_menu_entry *tmp;
+       int len;
+
+       len = strlen(text);
+
+       while ((tmp = &entry[*index])) {
+               (*index)++;
+
+               if (!tmp->cmd)
+                       break;
+
+               if (tmp->exists && !tmp->exists(data.menu))
+                       continue;
+
+               if (!strncmp(tmp->cmd, text, len))
+                       return strdup(tmp->cmd);
+       }
+
+       return NULL;
+}
+
+static char *cmd_generator(const char *text, int state)
+{
+       static int index;
+       static bool default_menu_enabled;
+       char *cmd;
+
+       if (!state) {
+               index = 0;
+               default_menu_enabled = true;
+       }
+
+       if (default_menu_enabled) {
+               cmd = find_cmd(text, default_menu, &index);
+               if (cmd) {
+                       return cmd;
+               } else {
+                       index = 0;
+                       default_menu_enabled = false;
+               }
+       }
+
+       return find_cmd(text, data.menu->entries, &index);
+}
+
+static char **menu_completion(const struct bt_shell_menu_entry *entry,
+                               const char *text, char *input_cmd)
+{
+       char **matches = NULL;
+
+       for (; entry->cmd; entry++) {
+               if (strcmp(entry->cmd, input_cmd))
+                       continue;
+
+               if (!entry->gen) {
+                       if (text[0] == '\0')
+                               bt_shell_printf("Usage: %s %s\n", entry->cmd,
+                                               entry->arg ? entry->arg : "");
+                       break;
+               }
+
+               rl_completion_display_matches_hook = entry->disp;
+               matches = rl_completion_matches(text, entry->gen);
+               break;
+       }
+
+       return matches;
+}
+
+static char **shell_completion(const char *text, int start, int end)
+{
+       char **matches = NULL;
+
+       if (!data.menu)
+               return NULL;
+
+       if (start > 0) {
+               wordexp_t w;
+
+               if (wordexp(rl_line_buffer, &w, WRDE_NOCMD))
+                       return NULL;
+
+               matches = menu_completion(default_menu, text, w.we_wordv[0]);
+               if (!matches)
+                       matches = menu_completion(data.menu->entries, text,
+                                                       w.we_wordv[0]);
+
+               wordfree(&w);
+       } else {
+               rl_completion_display_matches_hook = NULL;
+               matches = rl_completion_matches(text, cmd_generator);
+       }
+
+       if (!matches)
+               rl_attempted_completion_over = 1;
+
+       return matches;
+}
+
+static bool io_hup(struct io *io, void *user_data)
+{
+       g_main_loop_quit(main_loop);
+
+       return false;
+}
+
+static bool signal_read(struct io *io, void *user_data)
+{
+       static bool terminated = false;
+       struct signalfd_siginfo si;
+       ssize_t result;
+       int fd;
+
+       fd = io_get_fd(io);
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return false;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+               if (data.input) {
+                       rl_replace_line("", 0);
+                       rl_crlf();
+                       rl_on_new_line();
+                       rl_redisplay();
+                       break;
+               }
+
+               /*
+                * If input was not yet setup up that means signal was received
+                * while daemon was not yet running. Since user is not able
+                * to terminate client by CTRL-D or typing exit treat this as
+                * exit and fall through.
+                */
+
+               /* fall through */
+       case SIGTERM:
+               if (!terminated) {
+                       rl_replace_line("", 0);
+                       rl_crlf();
+                       g_main_loop_quit(main_loop);
+               }
+
+               terminated = true;
+               break;
+       }
+
+       return false;
+}
+
+static struct io *setup_signalfd(void)
+{
+       struct io *io;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Failed to set signal mask");
+               return 0;
+       }
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0) {
+               perror("Failed to create signal descriptor");
+               return 0;
+       }
+
+       io = io_new(fd);
+
+       io_set_close_on_destroy(io, true);
+       io_set_read_handler(io, signal_read, NULL, NULL);
+       io_set_disconnect_handler(io, io_hup, NULL, NULL);
+
+       return io;
+}
+
+static void rl_init(void)
+{
+       setlinebuf(stdout);
+       rl_attempted_completion_function = shell_completion;
+
+       rl_erase_empty_line = 1;
+       rl_callback_handler_install(NULL, rl_handler);
+}
+
+static const struct option main_options[] = {
+       { "version",    no_argument, 0, 'v' },
+       { "help",       no_argument, 0, 'h' },
+};
+
+static void usage(int argc, char **argv, const struct bt_shell_opt *opt)
+{
+       unsigned int i;
+
+       printf("%s ver %s\n", argv[0], VERSION);
+       printf("Usage:\n"
+               "\t%s [options]\n", argv[0]);
+
+       printf("Options:\n");
+
+       for (i = 0; opt && opt->options[i].name; i++)
+               printf("\t--%s \t%s\n", opt->options[i].name, opt->help[i]);
+
+       printf("\t--version \tDisplay version\n"
+               "\t--help \t\tDisplay help\n");
+}
+
+void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt)
+{
+       int c, index = 0;
+       struct option options[256];
+       char optstr[256];
+       size_t offset;
+
+       offset = sizeof(main_options) / sizeof(struct option);
+
+       memcpy(options, main_options, sizeof(struct option) * offset);
+
+       if (opt) {
+               memcpy(options + offset, opt->options,
+                               sizeof(struct option) * opt->optno);
+               snprintf(optstr, sizeof(optstr), "+hv%s", opt->optstr);
+       } else
+               snprintf(optstr, sizeof(optstr), "+hv");
+
+       while ((c = getopt_long(argc, argv, optstr, options, &index)) != -1) {
+               switch (c) {
+               case 'v':
+                       printf("%s: %s\n", argv[0], VERSION);
+                       exit(EXIT_SUCCESS);
+                       return;
+               case 'h':
+                       usage(argc, argv, opt);
+                       exit(EXIT_SUCCESS);
+                       return;
+               default:
+                       if (c != opt->options[index - offset].val) {
+                               usage(argc, argv, opt);
+                               exit(EXIT_SUCCESS);
+                               return;
+                       }
+
+                       *opt->optarg[index - offset] = optarg;
+               }
+       }
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       rl_init();
+}
+
+static void rl_cleanup(void)
+{
+       rl_message("");
+       rl_callback_handler_remove();
+}
+
+void bt_shell_run(void)
+{
+       struct io *signal;
+
+       signal = setup_signalfd();
+
+       g_main_loop_run(main_loop);
+
+       bt_shell_release_prompt("");
+       bt_shell_detach();
+
+       io_destroy(signal);
+
+       g_main_loop_unref(main_loop);
+       main_loop = NULL;
+
+       rl_cleanup();
+}
+
+bool bt_shell_set_menu(const struct bt_shell_menu *menu)
+{
+       if (!menu)
+               return false;
+
+       data.menu = menu;
+
+       if (!data.main)
+               data.main = menu;
+
+       return true;
+}
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu)
+{
+       if (!menu)
+               return false;
+
+       if (!data.submenus)
+               data.submenus = queue_new();
+
+       queue_push_tail(data.submenus, (void *) menu);
+
+       return true;
+}
+
+void bt_shell_set_prompt(const char *string)
+{
+       if (!main_loop)
+               return;
+
+       rl_set_prompt(string);
+       printf("\r");
+       rl_on_new_line();
+       rl_redisplay();
+}
+
+static bool input_read(struct io *io, void *user_data)
+{
+       rl_callback_read_char();
+       return true;
+}
+
+bool bt_shell_attach(int fd)
+{
+       struct io *io;
+
+       /* TODO: Allow more than one input? */
+       if (data.input)
+               return false;
+
+       io = io_new(fd);
+
+       io_set_read_handler(io, input_read, NULL, NULL);
+       io_set_disconnect_handler(io, io_hup, NULL, NULL);
+
+       data.input = io;
+
+       return true;
+}
+
+bool bt_shell_detach(void)
+{
+       if (!data.input)
+               return false;
+
+       io_destroy(data.input);
+       data.input = NULL;
+
+       return true;
+}
diff --git a/src/shared/shell.h b/src/shared/shell.h
new file mode 100644 (file)
index 0000000..8b8b1f6
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2017  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 <getopt.h>
+
+#define COLOR_OFF      "\x1B[0m"
+#define COLOR_RED      "\x1B[0;91m"
+#define COLOR_GREEN    "\x1B[0;92m"
+#define COLOR_YELLOW   "\x1B[0;93m"
+#define COLOR_BLUE     "\x1B[0;94m"
+#define COLOR_BOLDGRAY "\x1B[1;30m"
+#define COLOR_BOLDWHITE        "\x1B[1;37m"
+#define COLOR_HIGHLIGHT        "\x1B[1;39m"
+
+struct bt_shell_menu;
+
+typedef void (*bt_shell_menu_cb_t)(int argc, char *argv[]);
+typedef char * (*bt_shell_menu_gen_t)(const char *text, int state);
+typedef void (*bt_shell_menu_disp_t) (char **matches, int num_matches,
+                                                       int max_length);
+typedef void (*bt_shell_prompt_input_func) (const char *input, void *user_data);
+typedef bool (*bt_shell_menu_exists_t) (const struct bt_shell_menu *menu);
+
+struct bt_shell_menu_entry {
+       const char *cmd;
+       const char *arg;
+       bt_shell_menu_cb_t func;
+       const char *desc;
+       bt_shell_menu_gen_t gen;
+       bt_shell_menu_disp_t disp;
+       bt_shell_menu_exists_t exists;
+};
+
+struct bt_shell_menu {
+       const char *name;
+       const char *desc;
+       const struct bt_shell_menu_entry entries[];
+};
+
+struct bt_shell_opt {
+       const struct option *options;
+       size_t optno;
+       const char *optstr;
+       const char ***optarg;
+       const char **help;
+};
+
+void bt_shell_init(int argc, char **argv, const struct bt_shell_opt *opt);
+
+void bt_shell_run(void);
+
+bool bt_shell_set_menu(const struct bt_shell_menu *menu);
+
+bool bt_shell_add_submenu(const struct bt_shell_menu *menu);
+
+bool bt_shell_remove_submenu(const struct bt_shell_menu *menu);
+
+void bt_shell_set_prompt(const char *string);
+
+void bt_shell_printf(const char *fmt,
+                               ...) __attribute__((format(printf, 1, 2)));
+void bt_shell_hexdump(const unsigned char *buf, size_t len);
+
+void bt_shell_prompt_input(const char *label, const char *msg,
+                       bt_shell_prompt_input_func func, void *user_data);
+int bt_shell_release_prompt(const char *input);
+
+bool bt_shell_attach(int fd);
+bool bt_shell_detach(void);
+
+void bt_shell_cleanup(void);
index 7878552..f6f265e 100755 (executable)
@@ -28,6 +28,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -117,7 +118,7 @@ unsigned char util_get_dt(const char *parent, const char *name)
        char filename[PATH_MAX];
        struct stat st;
 
-       snprintf(filename, sizeof(filename), "%s/%s", parent, name);
+       snprintf(filename, PATH_MAX, "%s/%s", parent, name);
        if (lstat(filename, &st) == 0 && S_ISDIR(st.st_mode))
                return DT_DIR;
 
@@ -151,3 +152,831 @@ void util_clear_uid(unsigned int *bitmap, uint8_t id)
 
        *bitmap &= ~(1 << (id - 1));
 }
+
+static const struct {
+       uint16_t uuid;
+       const char *str;
+} uuid16_table[] = {
+       { 0x0001, "SDP"                                         },
+       { 0x0003, "RFCOMM"                                      },
+       { 0x0005, "TCS-BIN"                                     },
+       { 0x0007, "ATT"                                         },
+       { 0x0008, "OBEX"                                        },
+       { 0x000f, "BNEP"                                        },
+       { 0x0010, "UPNP"                                        },
+       { 0x0011, "HIDP"                                        },
+       { 0x0012, "Hardcopy Control Channel"                    },
+       { 0x0014, "Hardcopy Data Channel"                       },
+       { 0x0016, "Hardcopy Notification"                       },
+       { 0x0017, "AVCTP"                                       },
+       { 0x0019, "AVDTP"                                       },
+       { 0x001b, "CMTP"                                        },
+       { 0x001e, "MCAP Control Channel"                        },
+       { 0x001f, "MCAP Data Channel"                           },
+       { 0x0100, "L2CAP"                                       },
+       /* 0x0101 to 0x0fff undefined */
+       { 0x1000, "Service Discovery Server Service Class"      },
+       { 0x1001, "Browse Group Descriptor Service Class"       },
+       { 0x1002, "Public Browse Root"                          },
+       /* 0x1003 to 0x1100 undefined */
+       { 0x1101, "Serial Port"                                 },
+       { 0x1102, "LAN Access Using PPP"                        },
+       { 0x1103, "Dialup Networking"                           },
+       { 0x1104, "IrMC Sync"                                   },
+       { 0x1105, "OBEX Object Push"                            },
+       { 0x1106, "OBEX File Transfer"                          },
+       { 0x1107, "IrMC Sync Command"                           },
+       { 0x1108, "Headset"                                     },
+       { 0x1109, "Cordless Telephony"                          },
+       { 0x110a, "Audio Source"                                },
+       { 0x110b, "Audio Sink"                                  },
+       { 0x110c, "A/V Remote Control Target"                   },
+       { 0x110d, "Advanced Audio Distribution"                 },
+       { 0x110e, "A/V Remote Control"                          },
+       { 0x110f, "A/V Remote Control Controller"               },
+       { 0x1110, "Intercom"                                    },
+       { 0x1111, "Fax"                                         },
+       { 0x1112, "Headset AG"                                  },
+       { 0x1113, "WAP"                                         },
+       { 0x1114, "WAP Client"                                  },
+       { 0x1115, "PANU"                                        },
+       { 0x1116, "NAP"                                         },
+       { 0x1117, "GN"                                          },
+       { 0x1118, "Direct Printing"                             },
+       { 0x1119, "Reference Printing"                          },
+       { 0x111a, "Basic Imaging Profile"                       },
+       { 0x111b, "Imaging Responder"                           },
+       { 0x111c, "Imaging Automatic Archive"                   },
+       { 0x111d, "Imaging Referenced Objects"                  },
+       { 0x111e, "Handsfree"                                   },
+       { 0x111f, "Handsfree Audio Gateway"                     },
+       { 0x1120, "Direct Printing Refrence Objects Service"    },
+       { 0x1121, "Reflected UI"                                },
+       { 0x1122, "Basic Printing"                              },
+       { 0x1123, "Printing Status"                             },
+       { 0x1124, "Human Interface Device Service"              },
+       { 0x1125, "Hardcopy Cable Replacement"                  },
+       { 0x1126, "HCR Print"                                   },
+       { 0x1127, "HCR Scan"                                    },
+       { 0x1128, "Common ISDN Access"                          },
+       /* 0x1129 and 0x112a undefined */
+       { 0x112d, "SIM Access"                                  },
+       { 0x112e, "Phonebook Access Client"                     },
+       { 0x112f, "Phonebook Access Server"                     },
+       { 0x1130, "Phonebook Access"                            },
+       { 0x1131, "Headset HS"                                  },
+       { 0x1132, "Message Access Server"                       },
+       { 0x1133, "Message Notification Server"                 },
+       { 0x1134, "Message Access Profile"                      },
+       { 0x1135, "GNSS"                                        },
+       { 0x1136, "GNSS Server"                                 },
+       { 0x1137, "3D Display"                                  },
+       { 0x1138, "3D Glasses"                                  },
+       { 0x1139, "3D Synchronization"                          },
+       { 0x113a, "MPS Profile"                                 },
+       { 0x113b, "MPS Service"                                 },
+       /* 0x113c to 0x11ff undefined */
+       { 0x1200, "PnP Information"                             },
+       { 0x1201, "Generic Networking"                          },
+       { 0x1202, "Generic File Transfer"                       },
+       { 0x1203, "Generic Audio"                               },
+       { 0x1204, "Generic Telephony"                           },
+       { 0x1205, "UPNP Service"                                },
+       { 0x1206, "UPNP IP Service"                             },
+       { 0x1300, "UPNP IP PAN"                                 },
+       { 0x1301, "UPNP IP LAP"                                 },
+       { 0x1302, "UPNP IP L2CAP"                               },
+       { 0x1303, "Video Source"                                },
+       { 0x1304, "Video Sink"                                  },
+       { 0x1305, "Video Distribution"                          },
+       /* 0x1306 to 0x13ff undefined */
+       { 0x1400, "HDP"                                         },
+       { 0x1401, "HDP Source"                                  },
+       { 0x1402, "HDP Sink"                                    },
+       /* 0x1403 to 0x17ff undefined */
+       { 0x1800, "Generic Access Profile"                      },
+       { 0x1801, "Generic Attribute Profile"                   },
+       { 0x1802, "Immediate Alert"                             },
+       { 0x1803, "Link Loss"                                   },
+       { 0x1804, "Tx Power"                                    },
+       { 0x1805, "Current Time Service"                        },
+       { 0x1806, "Reference Time Update Service"               },
+       { 0x1807, "Next DST Change Service"                     },
+       { 0x1808, "Glucose"                                     },
+       { 0x1809, "Health Thermometer"                          },
+       { 0x180a, "Device Information"                          },
+       /* 0x180b and 0x180c undefined */
+       { 0x180d, "Heart Rate"                                  },
+       { 0x180e, "Phone Alert Status Service"                  },
+       { 0x180f, "Battery Service"                             },
+       { 0x1810, "Blood Pressure"                              },
+       { 0x1811, "Alert Notification Service"                  },
+       { 0x1812, "Human Interface Device"                      },
+       { 0x1813, "Scan Parameters"                             },
+       { 0x1814, "Running Speed and Cadence"                   },
+       { 0x1815, "Automation IO"                               },
+       { 0x1816, "Cycling Speed and Cadence"                   },
+       /* 0x1817 undefined */
+       { 0x1818, "Cycling Power"                               },
+       { 0x1819, "Location and Navigation"                     },
+       { 0x181a, "Environmental Sensing"                       },
+       { 0x181b, "Body Composition"                            },
+       { 0x181c, "User Data"                                   },
+       { 0x181d, "Weight Scale"                                },
+       { 0x181e, "Bond Management"                             },
+       { 0x181f, "Continuous Glucose Monitoring"               },
+       { 0x1820, "Internet Protocol Support"                   },
+       { 0x1821, "Indoor Positioning"                          },
+       { 0x1822, "Pulse Oximeter"                              },
+       { 0x1823, "HTTP Proxy"                                  },
+       { 0x1824, "Transport Discovery"                         },
+       { 0x1825, "Object Transfer"                             },
+       { 0x1826, "Fitness Machine"                             },
+       { 0x1827, "Mesh Provisioning"                           },
+       { 0x1828, "Mesh Proxy"                                  },
+       /* 0x1829 to 0x27ff undefined */
+       { 0x2800, "Primary Service"                             },
+       { 0x2801, "Secondary Service"                           },
+       { 0x2802, "Include"                                     },
+       { 0x2803, "Characteristic"                              },
+       /* 0x2804 to 0x28ff undefined */
+       { 0x2900, "Characteristic Extended Properties"          },
+       { 0x2901, "Characteristic User Description"             },
+       { 0x2902, "Client Characteristic Configuration"         },
+       { 0x2903, "Server Characteristic Configuration"         },
+       { 0x2904, "Characteristic Format"                       },
+       { 0x2905, "Characteristic Aggregate Formate"            },
+       { 0x2906, "Valid Range"                                 },
+       { 0x2907, "External Report Reference"                   },
+       { 0x2908, "Report Reference"                            },
+       { 0x2909, "Number of Digitals"                          },
+       { 0x290a, "Value Trigger Setting"                       },
+       { 0x290b, "Environmental Sensing Configuration"         },
+       { 0x290c, "Environmental Sensing Measurement"           },
+       { 0x290d, "Environmental Sensing Trigger Setting"       },
+       { 0x290e, "Time Trigger Setting"                        },
+       /* 0x290f to 0x29ff undefined */
+       { 0x2a00, "Device Name"                                 },
+       { 0x2a01, "Appearance"                                  },
+       { 0x2a02, "Peripheral Privacy Flag"                     },
+       { 0x2a03, "Reconnection Address"                        },
+       { 0x2a04, "Peripheral Preferred Connection Parameters"  },
+       { 0x2a05, "Service Changed"                             },
+       { 0x2a06, "Alert Level"                                 },
+       { 0x2a07, "Tx Power Level"                              },
+       { 0x2a08, "Date Time"                                   },
+       { 0x2a09, "Day of Week"                                 },
+       { 0x2a0a, "Day Date Time"                               },
+       /* 0x2a0b undefined */
+       { 0x2a0c, "Exact Time 256"                              },
+       { 0x2a0d, "DST Offset"                                  },
+       { 0x2a0e, "Time Zone"                                   },
+       { 0x2a0f, "Local Time Information"                      },
+       /* 0x2a10 undefined */
+       { 0x2a11, "Time with DST"                               },
+       { 0x2a12, "Time Accuracy"                               },
+       { 0x2a13, "Time Source"                                 },
+       { 0x2a14, "Reference Time Information"                  },
+       /* 0x2a15 undefined */
+       { 0x2a16, "Time Update Control Point"                   },
+       { 0x2a17, "Time Update State"                           },
+       { 0x2a18, "Glucose Measurement"                         },
+       { 0x2a19, "Battery Level"                               },
+       /* 0x2a1a and 0x2a1b undefined */
+       { 0x2a1c, "Temperature Measurement"                     },
+       { 0x2a1d, "Temperature Type"                            },
+       { 0x2a1e, "Intermediate Temperature"                    },
+       /* 0x2a1f and 0x2a20 undefined */
+       { 0x2a21, "Measurement Interval"                        },
+       { 0x2a22, "Boot Keyboard Input Report"                  },
+       { 0x2a23, "System ID"                                   },
+       { 0x2a24, "Model Number String"                         },
+       { 0x2a25, "Serial Number String"                        },
+       { 0x2a26, "Firmware Revision String"                    },
+       { 0x2a27, "Hardware Revision String"                    },
+       { 0x2a28, "Software Revision String"                    },
+       { 0x2a29, "Manufacturer Name String"                    },
+       { 0x2a2a, "IEEE 11073-20601 Regulatory Cert. Data List" },
+       { 0x2a2b, "Current Time"                                },
+       { 0x2a2c, "Magnetic Declination"                        },
+       /* 0x2a2d to 0x2a30 undefined */
+       { 0x2a31, "Scan Refresh"                                },
+       { 0x2a32, "Boot Keyboard Output Report"                 },
+       { 0x2a33, "Boot Mouse Input Report"                     },
+       { 0x2a34, "Glucose Measurement Context"                 },
+       { 0x2a35, "Blood Pressure Measurement"                  },
+       { 0x2a36, "Intermediate Cuff Pressure"                  },
+       { 0x2a37, "Heart Rate Measurement"                      },
+       { 0x2a38, "Body Sensor Location"                        },
+       { 0x2a39, "Heart Rate Control Point"                    },
+       /* 0x2a3a to 0x2a3e undefined */
+       { 0x2a3f, "Alert Status"                                },
+       { 0x2a40, "Ringer Control Point"                        },
+       { 0x2a41, "Ringer Setting"                              },
+       { 0x2a42, "Alert Category ID Bit Mask"                  },
+       { 0x2a43, "Alert Category ID"                           },
+       { 0x2a44, "Alert Notification Control Point"            },
+       { 0x2a45, "Unread Alert Status"                         },
+       { 0x2a46, "New Alert"                                   },
+       { 0x2a47, "Supported New Alert Category"                },
+       { 0x2a48, "Supported Unread Alert Category"             },
+       { 0x2a49, "Blood Pressure Feature"                      },
+       { 0x2a4a, "HID Information"                             },
+       { 0x2a4b, "Report Map"                                  },
+       { 0x2a4c, "HID Control Point"                           },
+       { 0x2a4d, "Report"                                      },
+       { 0x2a4e, "Protocol Mode"                               },
+       { 0x2a4f, "Scan Interval Window"                        },
+       { 0x2a50, "PnP ID"                                      },
+       { 0x2a51, "Glucose Feature"                             },
+       { 0x2a52, "Record Access Control Point"                 },
+       { 0x2a53, "RSC Measurement"                             },
+       { 0x2a54, "RSC Feature"                                 },
+       { 0x2a55, "SC Control Point"                            },
+       { 0x2a56, "Digital"                                     },
+       /* 0x2a57 undefined */
+       { 0x2a58, "Analog"                                      },
+       /* 0x2a59 undefined */
+       { 0x2a5a, "Aggregate"                                   },
+       { 0x2a5b, "CSC Measurement"                             },
+       { 0x2a5c, "CSC Feature"                                 },
+       { 0x2a5d, "Sensor Location"                             },
+       /* 0x2a5e to 0x2a62 undefined */
+       { 0x2a63, "Cycling Power Measurement"                   },
+       { 0x2a64, "Cycling Power Vector"                        },
+       { 0x2a65, "Cycling Power Feature"                       },
+       { 0x2a66, "Cycling Power Control Point"                 },
+       { 0x2a67, "Location and Speed"                          },
+       { 0x2a68, "Navigation"                                  },
+       { 0x2a69, "Position Quality"                            },
+       { 0x2a6a, "LN Feature"                                  },
+       { 0x2a6b, "LN Control Point"                            },
+       { 0x2a6c, "Elevation"                                   },
+       { 0x2a6d, "Pressure"                                    },
+       { 0x2a6e, "Temperature"                                 },
+       { 0x2a6f, "Humidity"                                    },
+       { 0x2a70, "True Wind Speed"                             },
+       { 0x2a71, "True Wind Direction"                         },
+       { 0x2a72, "Apparent Wind Speed"                         },
+       { 0x2a73, "Apparent Wind Direction"                     },
+       { 0x2a74, "Gust Factor"                                 },
+       { 0x2a75, "Pollen Concentration"                        },
+       { 0x2a76, "UV Index"                                    },
+       { 0x2a77, "Irradiance"                                  },
+       { 0x2a78, "Rainfall"                                    },
+       { 0x2a79, "Wind Chill"                                  },
+       { 0x2a7a, "Heat Index"                                  },
+       { 0x2a7b, "Dew Point"                                   },
+       { 0x2a7c, "Trend"                                       },
+       { 0x2a7d, "Descriptor Value Changed"                    },
+       { 0x2a7e, "Aerobic Heart Rate Lower Limit"              },
+       { 0x2a7f, "Aerobic Threshold"                           },
+       { 0x2a80, "Age"                                         },
+       { 0x2a81, "Anaerobic Heart Rate Lower Limit"            },
+       { 0x2a82, "Anaerobic Heart Rate Upper Limit"            },
+       { 0x2a83, "Anaerobic Threshold"                         },
+       { 0x2a84, "Aerobic Heart Rate Upper Limit"              },
+       { 0x2a85, "Date of Birth"                               },
+       { 0x2a86, "Date of Threshold Assessment"                },
+       { 0x2a87, "Email Address"                               },
+       { 0x2a88, "Fat Burn Heart Rate Lower Limit"             },
+       { 0x2a89, "Fat Burn Heart Rate Upper Limit"             },
+       { 0x2a8a, "First Name"                                  },
+       { 0x2a8b, "Five Zone Heart Rate Limits"                 },
+       { 0x2a8c, "Gender"                                      },
+       { 0x2a8d, "Heart Rate Max"                              },
+       { 0x2a8e, "Height"                                      },
+       { 0x2a8f, "Hip Circumference"                           },
+       { 0x2a90, "Last Name"                                   },
+       { 0x2a91, "Maximum Recommended Heart Rate"              },
+       { 0x2a92, "Resting Heart Rate"                          },
+       { 0x2a93, "Sport Type for Aerobic/Anaerobic Thresholds" },
+       { 0x2a94, "Three Zone Heart Rate Limits"                },
+       { 0x2a95, "Two Zone Heart Rate Limit"                   },
+       { 0x2a96, "VO2 Max"                                     },
+       { 0x2a97, "Waist Circumference"                         },
+       { 0x2a98, "Weight"                                      },
+       { 0x2a99, "Database Change Increment"                   },
+       { 0x2a9a, "User Index"                                  },
+       { 0x2a9b, "Body Composition Feature"                    },
+       { 0x2a9c, "Body Composition Measurement"                },
+       { 0x2a9d, "Weight Measurement"                          },
+       { 0x2a9e, "Weight Scale Feature"                        },
+       { 0x2a9f, "User Control Point"                          },
+       { 0x2aa0, "Magnetic Flux Density - 2D"                  },
+       { 0x2aa1, "Magnetic Flux Density - 3D"                  },
+       { 0x2aa2, "Language"                                    },
+       { 0x2aa3, "Barometric Pressure Trend"                   },
+       { 0x2aa4, "Bond Management Control Point"               },
+       { 0x2aa5, "Bond Management Feature"                     },
+       { 0x2aa6, "Central Address Resolution"                  },
+       { 0x2aa7, "CGM Measurement"                             },
+       { 0x2aa8, "CGM Feature"                                 },
+       { 0x2aa9, "CGM Status"                                  },
+       { 0x2aaa, "CGM Session Start Time"                      },
+       { 0x2aab, "CGM Session Run Time"                        },
+       { 0x2aac, "CGM Specific Ops Control Point"              },
+       { 0x2aad, "Indoor Positioning Configuration"            },
+       { 0x2aae, "Latitude"                                    },
+       { 0x2aaf, "Longitude"                                   },
+       { 0x2ab0, "Local North Coordinate"                      },
+       { 0x2ab1, "Local East Coordinate"                       },
+       { 0x2ab2, "Floor Number"                                },
+       { 0x2ab3, "Altitude"                                    },
+       { 0x2ab4, "Uncertainty"                                 },
+       { 0x2ab5, "Location Name"                               },
+       { 0x2ab6, "URI"                                         },
+       { 0x2ab7, "HTTP Headers"                                },
+       { 0x2ab8, "HTTP Status Code"                            },
+       { 0x2ab9, "HTTP Entity Body"                            },
+       { 0x2aba, "HTTP Control Point"                          },
+       { 0x2abb, "HTTPS Security"                              },
+       { 0x2abc, "TDS Control Point"                           },
+       { 0x2abd, "OTS Feature"                                 },
+       { 0x2abe, "Object Name"                                 },
+       { 0x2abf, "Object Type"                                 },
+       { 0x2ac0, "Object Size"                                 },
+       { 0x2ac1, "Object First-Created"                        },
+       { 0x2ac2, "Object Last-Modified"                        },
+       { 0x2ac3, "Object ID"                                   },
+       { 0x2ac4, "Object Properties"                           },
+       { 0x2ac5, "Object Action Control Point"                 },
+       { 0x2ac6, "Object List Control Point"                   },
+       { 0x2ac7, "Object List Filter"                          },
+       { 0x2ac8, "Object Changed"                              },
+       { 0x2ac9, "Resolvable Private Address Only"             },
+       /* 0x2aca and 0x2acb undefined */
+       { 0x2acc, "Fitness Machine Feature"                     },
+       { 0x2acd, "Treadmill Data"                              },
+       { 0x2ace, "Cross Trainer Data"                          },
+       { 0x2acf, "Step Climber Data"                           },
+       { 0x2ad0, "Stair Climber Data"                          },
+       { 0x2ad1, "Rower Data"                                  },
+       { 0x2ad2, "Indoor Bike Data"                            },
+       { 0x2ad3, "Training Status"                             },
+       { 0x2ad4, "Supported Speed Range"                       },
+       { 0x2ad5, "Supported Inclination Range"                 },
+       { 0x2ad6, "Supported Resistance Level Range"            },
+       { 0x2ad7, "Supported Heart Rate Range"                  },
+       { 0x2ad8, "Supported Power Range"                       },
+       { 0x2ad9, "Fitness Machine Control Point"               },
+       { 0x2ada, "Fitness Machine Status"                      },
+       { 0x2adb, "Mesh Provisioning Data In"                   },
+       { 0x2adc, "Mesh Provisioning Data Out"                  },
+       { 0x2add, "Mesh Proxy Data In"                          },
+       { 0x2ade, "Mesh Proxy Data Out"                         },
+       /* vendor defined */
+       { 0xfeff, "GN Netcom"                                   },
+       { 0xfefe, "GN ReSound A/S"                              },
+       { 0xfefd, "Gimbal, Inc."                                },
+       { 0xfefc, "Gimbal, Inc."                                },
+       { 0xfefb, "Stollmann E+V GmbH"                          },
+       { 0xfefa, "PayPal, Inc."                                },
+       { 0xfef9, "PayPal, Inc."                                },
+       { 0xfef8, "Aplix Corporation"                           },
+       { 0xfef7, "Aplix Corporation"                           },
+       { 0xfef6, "Wicentric, Inc."                             },
+       { 0xfef5, "Dialog Semiconductor GmbH"                   },
+       { 0xfef4, "Google"                                      },
+       { 0xfef3, "Google"                                      },
+       { 0xfef2, "CSR"                                         },
+       { 0xfef1, "CSR"                                         },
+       { 0xfef0, "Intel"                                       },
+       { 0xfeef, "Polar Electro Oy"                            },
+       { 0xfeee, "Polar Electro Oy"                            },
+       { 0xfeed, "Tile, Inc."                                  },
+       { 0xfeec, "Tile, Inc."                                  },
+       { 0xfeeb, "Swirl Networks, Inc."                        },
+       { 0xfeea, "Swirl Networks, Inc."                        },
+       { 0xfee9, "Quintic Corp."                               },
+       { 0xfee8, "Quintic Corp."                               },
+       { 0xfee7, "Tencent Holdings Limited"                    },
+       { 0xfee6, "Seed Labs, Inc."                             },
+       { 0xfee5, "Nordic Semiconductor ASA"                    },
+       { 0xfee4, "Nordic Semiconductor ASA"                    },
+       { 0xfee3, "Anki, Inc."                                  },
+       { 0xfee2, "Anki, Inc."                                  },
+       { 0xfee1, "Anhui Huami Information Technology Co."      },
+       { 0xfee0, "Anhui Huami Information Technology Co."      },
+       { 0xfedf, "Design SHIFT"                                },
+       { 0xfede, "Coin, Inc."                                  },
+       { 0xfedd, "Jawbone"                                     },
+       { 0xfedc, "Jawbone"                                     },
+       { 0xfedb, "Perka, Inc."                                 },
+       { 0xfeda, "ISSC Technologies Corporation"               },
+       { 0xfed9, "Pebble Technology Corporation"               },
+       { 0xfed8, "Google"                                      },
+       { 0xfed7, "Broadcom Corporation"                        },
+       { 0xfed6, "Broadcom Corporation"                        },
+       { 0xfed5, "Plantronics Inc."                            },
+       { 0xfed4, "Apple, Inc."                                 },
+       { 0xfed3, "Apple, Inc."                                 },
+       { 0xfed2, "Apple, Inc."                                 },
+       { 0xfed1, "Apple, Inc."                                 },
+       { 0xfed0, "Apple, Inc."                                 },
+       { 0xfecf, "Apple, Inc."                                 },
+       { 0xfece, "Apple, Inc."                                 },
+       { 0xfecd, "Apple, Inc."                                 },
+       { 0xfecc, "Apple, Inc."                                 },
+       { 0xfecb, "Apple, Inc."                                 },
+       { 0xfeca, "Apple, Inc."                                 },
+       { 0xfec9, "Apple, Inc."                                 },
+       { 0xfec8, "Apple, Inc."                                 },
+       { 0xfec7, "Apple, Inc."                                 },
+       { 0xfec6, "Kocomojo, LLC"                               },
+       { 0xfec5, "Realtek Semiconductor Corp."                 },
+       { 0xfec4, "PLUS Location Systems"                       },
+       { 0xfec3, "360fly, Inc."                                },
+       { 0xfec2, "Blue Spark Technologies, Inc."               },
+       { 0xfec1, "KDDI Corporation"                            },
+       { 0xfec0, "KDDI Corporation"                            },
+       { 0xfebf, "Nod, Inc."                                   },
+       { 0xfebe, "Bose Corporation"                            },
+       { 0xfebd, "Clover Network, Inc."                        },
+       { 0xfebc, "Dexcom, Inc."                                },
+       { 0xfebb, "adafruit industries"                         },
+       { 0xfeba, "Tencent Holdings Limited"                    },
+       { 0xfeb9, "LG Electronics"                              },
+       { 0xfeb8, "Facebook, Inc."                              },
+       { 0xfeb7, "Facebook, Inc."                              },
+       { 0xfeb6, "Vencer Co, Ltd"                              },
+       { 0xfeb5, "WiSilica Inc."                               },
+       { 0xfeb4, "WiSilica Inc."                               },
+       { 0xfeb3, "Taobao"                                      },
+       { 0xfeb2, "Microsoft Corporation"                       },
+       { 0xfeb1, "Electronics Tomorrow Limited"                },
+       { 0xfeb0, "Nest Labs Inc."                              },
+       { 0xfeaf, "Nest Labs Inc."                              },
+       { 0xfeae, "Nokia Corporation"                           },
+       { 0xfead, "Nokia Corporation"                           },
+       { 0xfeac, "Nokia Corporation"                           },
+       { 0xfeab, "Nokia Corporation"                           },
+       { 0xfeaa, "Google"                                      },
+       { 0xfea9, "Savant Systems LLC"                          },
+       { 0xfea8, "Savant Systems LLC"                          },
+       { 0xfea7, "UTC Fire and Security"                       },
+       { 0xfea6, "GoPro, Inc."                                 },
+       { 0xfea5, "GoPro, Inc."                                 },
+       { 0xfea4, "Paxton Access Ltd"                           },
+       { 0xfea3, "ITT Industries"                              },
+       { 0xfea2, "Intrepid Control Systems, Inc."              },
+       { 0xfea1, "Intrepid Control Systems, Inc."              },
+       { 0xfea0, "Google"                                      },
+       { 0xfe9f, "Google"                                      },
+       { 0xfe9e, "Dialog Semiconductor B.V."                   },
+       { 0xfe9d, "Mobiquity Networks Inc"                      },
+       { 0xfe9c, "GSI Laboratories, Inc."                      },
+       { 0xfe9b, "Samsara Networks, Inc"                       },
+       { 0xfe9a, "Estimote"                                    },
+       { 0xfe99, "Currant, Inc."                               },
+       { 0xfe98, "Currant, Inc."                               },
+       { 0xfe97, "Tesla Motor Inc."                            },
+       { 0xfe96, "Tesla Motor Inc."                            },
+       { 0xfe95, "Xiaomi Inc."                                 },
+       { 0xfe94, "OttoQ Inc."                                  },
+       { 0xfe93, "OttoQ Inc."                                  },
+       { 0xfe92, "Jarden Safety & Security"                    },
+       { 0xfe91, "Shanghai Imilab Technology Co.,Ltd"          },
+       { 0xfe90, "JUMA"                                        },
+       { 0xfe8f, "CSR"                                         },
+       { 0xfe8e, "ARM Ltd"                                     },
+       { 0xfe8d, "Interaxon Inc."                              },
+       { 0xfe8c, "TRON Forum"                                  },
+       { 0xfe8b, "Apple, Inc."                                 },
+       { 0xfe8a, "Apple, Inc."                                 },
+       { 0xfe89, "B&O Play A/S"                                },
+       { 0xfe88, "SALTO SYSTEMS S.L."                          },
+       { 0xfe87, "Qingdao Yeelink Information Technology Co., Ltd. ( 青岛亿联客信息技术有限公司 )"        },
+       { 0xfe86, "HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 )"                                  },
+       { 0xfe85, "RF Digital Corp"                             },
+       { 0xfe84, "RF Digital Corp"                             },
+       { 0xfe83, "Blue Bite"                                   },
+       { 0xfe82, "Medtronic Inc."                              },
+       { 0xfe81, "Medtronic Inc."                              },
+       { 0xfe80, "Doppler Lab"                                 },
+       { 0xfe7f, "Doppler Lab"                                 },
+       { 0xfe7e, "Awear Solutions Ltd"                         },
+       { 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"                           },
+       { 0xfe65, "CHIPOLO d.o.o."                              },
+       { 0xfe64, "Siemens AG"                                  },
+       { 0xfe63, "Connected Yard, Inc."                        },
+       { 0xfe62, "Indagem Tech LLC"                            },
+       { 0xfe61, "Logitech International SA"                   },
+       { 0xfe60, "Lierda Science & Technology Group Co., Ltd." },
+       { 0xfe5F, "Eyefi, Inc."                                 },
+       { 0xfe5E, "Plastc Corporation"                          },
+       { 0xfe5D, "Grundfos A/S"                                },
+       { 0xfe5C, "million hunters GmbH"                        },
+       { 0xfe5B, "GT-tronics HK Ltd"                           },
+       { 0xfe5A, "Chronologics Corporation"                    },
+       { 0xfe59, "Nordic Semiconductor ASA"                    },
+       { 0xfe58, "Nordic Semiconductor ASA"                    },
+       { 0xfe57, "Dotted Labs"                                 },
+       { 0xfe56, "Google Inc."                                 },
+       { 0xfe55, "Google Inc."                                 },
+       { 0xfe54, "Motiv, Inc."                                 },
+       { 0xfe53, "3M"                                          },
+       { 0xfe52, "SetPoint Medical"                            },
+       { 0xfe51, "SRAM"                                        },
+       { 0xfe50, "Google Inc."                                 },
+       { 0xfe4F, "Molekule, Inc."                              },
+       { 0xfe4E, "NTT docomo"                                  },
+       { 0xfe4D, "Casambi Technologies Oy"                     },
+       { 0xfe4C, "Volkswagen AG"                               },
+       { 0xfe4B, "Koninklijke Philips N.V."                    },
+       { 0xfe4A, "OMRON HEALTHCARE Co., Ltd."                  },
+       { 0xfe49, "SenionLab AB"                                },
+       { 0xfe48, "General Motors"                              },
+       { 0xfe47, "General Motors"                              },
+       { 0xfe46, "B&O Play A/S"                                },
+       { 0xfe45, "Snapchat Inc"                                },
+       { 0xfe44, "SK Telecom"                                  },
+       { 0xfe43, "Andreas Stihl AG & Co. KG"                   },
+       { 0xfe42, "Nets A/S"                                    },
+       { 0xfe41, "Inugo Systems Limited"                       },
+       { 0xfe40, "Inugo Systems Limited"                       },
+       { 0xfe3F, "Friday Labs Limited"                         },
+       { 0xfe3E, "BD Medical"                                  },
+       { 0xfe3D, "BD Medical"                                  },
+       { 0xfe3C, "Alibaba"                                     },
+       { 0xfe3B, "Dolby Laboratories"                          },
+       { 0xfe3A, "TTS Tooltechnic Systems AG & Co. KG"         },
+       { 0xfe39, "TTS Tooltechnic Systems AG & Co. KG"         },
+       { 0xfe38, "Spaceek LTD"                                 },
+       { 0xfe37, "Spaceek LTD"                                 },
+       { 0xfe36, "HUAWEI Technologies Co., Ltd"                },
+       { 0xfe35, "HUAWEI Technologies Co., Ltd"                },
+       { 0xfe34, "SmallLoop LLC"                               },
+       { 0xfe33, "CHIPOLO d.o.o."                              },
+       { 0xfe32, "Pro-Mark, Inc."                              },
+       { 0xfe31, "Volkswagen AG"                               },
+       { 0xfe30, "Volkswagen AG"                               },
+       { 0xfe2F, "CRESCO Wireless, Inc"                        },
+       { 0xfe2E, "ERi,Inc."                                    },
+       { 0xfe2D, "SMART INNOVATION Co.,Ltd"                    },
+       { 0xfe2C, "Google Inc."                                 },
+       { 0xfe2B, "ITT Industries"                              },
+       { 0xfe2A, "DaisyWorks, Inc."                            },
+       { 0xfe29, "Gibson Innovations"                          },
+       { 0xfe28, "Ayla Network"                                },
+       { 0xfe27, "Google Inc."                                 },
+       { 0xfe26, "Google Inc."                                 },
+       { 0xfe25, "Apple, Inc."                                 },
+       { 0xfe24, "August Home Inc"                             },
+       { 0xfe23, "Zoll Medical Corporation"                    },
+       { 0xfe22, "Zoll Medical Corporation"                    },
+       { 0xfe21, "Bose Corporation"                            },
+       { 0xfe20, "Emerson"                                     },
+       { 0xfe1F, "Garmin International, Inc."                  },
+       { 0xfe1E, "Smart Innovations Co., Ltd"                  },
+       { 0xfe1D, "Illuminati Instrument Corporation"           },
+       { 0xfe1C, "NetMedia, Inc."                              },
+       /* SDO defined */
+       { 0xfffc, "AirFuel Alliance"                            },
+       { 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"                         },
+       /* BBC micro:bit Bluetooth Profiles */
+       { "e95d0753-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Accelerometer Service"                        },
+       { "e95dca4b-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Accelerometer Data"                           },
+       { "e95dfb24-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Accelerometer Period"                         },
+       { "e95df2d8-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Magnetometer Service"                         },
+       { "e95dfb11-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Magnetometer Data"                            },
+       { "e95d386c-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Magnetometer Period"                          },
+       { "e95d9715-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Magnetometer Bearing"                         },
+       { "e95d9882-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Button Service"                               },
+       { "e95dda90-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Button A State"                               },
+       { "e95dda91-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Button B State"                               },
+       { "e95d127b-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit IO PIN Service"                               },
+       { "e95d8d00-251d-470a-a062-fa1922dfa9a8", "MicroBit PIN Data"   },
+       { "e95d5899-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit PIN AD Configuration"                         },
+       { "e95dd822-251d-470a-a062-fa1922dfa9a8", "MicroBit PWM Control" },
+       { "e95dd91d-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Service" },
+       { "e95d7b77-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Matrix state" },
+       { "e95d93ee-251d-470a-a062-fa1922dfa9a8", "MicroBit LED Text"   },
+       { "e95d0d2d-251d-470a-a062-fa1922dfa9a8", "MicroBit Scrolling Delay" },
+       { "e95d93af-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Service" },
+       { "e95db84c-251d-470a-a062-fa1922dfa9a8", "MicroBit Requirements" },
+       { "e95d9775-251d-470a-a062-fa1922dfa9a8", "MicroBit Event Data" },
+       { "e95d23c4-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Client Requirements"                          },
+       { "e95d5404-251d-470a-a062-fa1922dfa9a8", "MicroBit Client Events" },
+       { "e95d93b0-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit DFU Control Service"                          },
+       { "e95d93b1-251d-470a-a062-fa1922dfa9a8", "MicroBit DFU Control" },
+       { "e95d6100-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Temperature Service"                          },
+       { "e95d1b25-251d-470a-a062-fa1922dfa9a8",
+               "MicroBit Temperature Period"                           },
+       /* Nordic UART Port Emulation */
+       { "6e400001-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART Service" },
+       { "6e400002-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART TX"      },
+       { "6e400003-b5a3-f393-e0a9-e50e24dcca9e", "Nordic UART RX"      },
+       { }
+};
+
+const char *bt_uuid16_to_str(uint16_t uuid)
+{
+       int i;
+
+       for (i = 0; uuid16_table[i].str; i++) {
+               if (uuid16_table[i].uuid == uuid)
+                       return uuid16_table[i].str;
+       }
+
+       return "Unknown";
+}
+
+const char *bt_uuid32_to_str(uint32_t uuid)
+{
+       if ((uuid & 0xffff0000) == 0x0000)
+               return bt_uuid16_to_str(uuid & 0x0000ffff);
+
+       return "Unknown";
+}
+
+const char *bt_uuidstr_to_str(const char *uuid)
+{
+       uint32_t val;
+       size_t len;
+       int i;
+
+       if (!uuid)
+               return NULL;
+
+       len = strlen(uuid);
+
+       if (len < 36) {
+               char *endptr = NULL;
+
+               val = strtol(uuid, &endptr, 0);
+               if (!endptr || *endptr != '\0')
+                       return NULL;
+
+               if (val > UINT16_MAX)
+                       return bt_uuid32_to_str(val);
+
+               return bt_uuid16_to_str(val);
+       }
+
+       if (len != 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";
+
+       if (sscanf(uuid, "%08x-0000-1000-8000-00805f9b34fb", &val) != 1)
+               return NULL;
+
+       return bt_uuid32_to_str(val);
+}
+
+static const struct {
+       uint16_t val;
+       bool generic;
+       const char *str;
+} appearance_table[] = {
+       {    0, true,  "Unknown"                },
+       {   64, true,  "Phone"                  },
+       {  128, true,  "Computer"               },
+       {  192, true,  "Watch"                  },
+       {  193, false, "Sports Watch"           },
+       {  256, true,  "Clock"                  },
+       {  320, true,  "Display"                },
+       {  384, true,  "Remote Control"         },
+       {  448, true,  "Eye-glasses"            },
+       {  512, true,  "Tag"                    },
+       {  576, true,  "Keyring"                },
+       {  640, true,  "Media Player"           },
+       {  704, true,  "Barcode Scanner"        },
+       {  768, true,  "Thermometer"            },
+       {  769, false, "Thermometer: Ear"       },
+       {  832, true,  "Heart Rate Sensor"      },
+       {  833, false, "Heart Rate Belt"        },
+       {  896, true,  "Blood Pressure"         },
+       {  897, false, "Blood Pressure: Arm"    },
+       {  898, false, "Blood Pressure: Wrist"  },
+       {  960, true,  "Human Interface Device" },
+       {  961, false, "Keyboard"               },
+       {  962, false, "Mouse"                  },
+       {  963, false, "Joystick"               },
+       {  964, false, "Gamepad"                },
+       {  965, false, "Digitizer Tablet"       },
+       {  966, false, "Card Reader"            },
+       {  967, false, "Digital Pen"            },
+       {  968, false, "Barcode Scanner"        },
+       { 1024, true,  "Glucose Meter"          },
+       { 1088, true,  "Running Walking Sensor"                 },
+       { 1089, false, "Running Walking Sensor: In-Shoe"        },
+       { 1090, false, "Running Walking Sensor: On-Shoe"        },
+       { 1091, false, "Running Walking Sensor: On-Hip"         },
+       { 1152, true,  "Cycling"                                },
+       { 1153, false, "Cycling: Cycling Computer"              },
+       { 1154, false, "Cycling: Speed Sensor"                  },
+       { 1155, false, "Cycling: Cadence Sensor"                },
+       { 1156, false, "Cycling: Power Sensor"                  },
+       { 1157, false, "Cycling: Speed and Cadence Sensor"      },
+       { 1216, true,  "Undefined"                              },
+
+       { 3136, true,  "Pulse Oximeter"                         },
+       { 3137, false, "Pulse Oximeter: Fingertip"              },
+       { 3138, false, "Pulse Oximeter: Wrist Worn"             },
+       { 3200, true,  "Weight Scale"                           },
+       { 3264, true,  "Undefined"                              },
+
+       { 5184, true,  "Outdoor Sports Activity"                },
+       { 5185, false, "Location Display Device"                },
+       { 5186, false, "Location and Navigation Display Device" },
+       { 5187, false, "Location Pod"                           },
+       { 5188, false, "Location and Navigation Pod"            },
+       { 5248, true,  "Undefined"                              },
+       { }
+};
+
+const char *bt_appear_to_str(uint16_t appearance)
+{
+       const char *str = NULL;
+       int i, type = 0;
+
+       for (i = 0; appearance_table[i].str; i++) {
+               if (appearance_table[i].generic) {
+                       if (appearance < appearance_table[i].val)
+                               break;
+                       type = i;
+               }
+
+               if (appearance_table[i].val == appearance) {
+                       str = appearance_table[i].str;
+                       break;
+               }
+       }
+
+       if (!str)
+               str = appearance_table[type].str;
+
+       return str;
+}
index 7d28ae9..3f5f6df 100755 (executable)
@@ -108,6 +108,11 @@ unsigned char util_get_dt(const char *parent, const char *name);
 uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
 void util_clear_uid(unsigned int *bitmap, uint8_t id);
 
+const char *bt_uuid16_to_str(uint16_t uuid);
+const char *bt_uuid32_to_str(uint32_t uuid);
+const char *bt_uuidstr_to_str(const char *uuid);
+const char *bt_appear_to_str(uint16_t appearance);
+
 static inline int8_t get_s8(const void *ptr)
 {
        return *((int8_t *) ptr);
diff --git a/test/advertisement-example b/test/advertisement-example
deleted file mode 100755 (executable)
index 98aeafa..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/python
-
-import dbus
-import dbus.exceptions
-import dbus.mainloop.glib
-import dbus.service
-
-import array
-import gobject
-
-from random import randint
-
-mainloop = None
-
-BLUEZ_SERVICE_NAME = 'org.bluez'
-LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
-DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
-DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
-
-LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
-
-
-class InvalidArgsException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
-
-
-class NotSupportedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.NotSupported'
-
-
-class NotPermittedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.NotPermitted'
-
-
-class InvalidValueLengthException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
-
-
-class FailedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.Failed'
-
-
-class Advertisement(dbus.service.Object):
-    PATH_BASE = '/org/bluez/example/advertisement'
-
-    def __init__(self, bus, index, advertising_type):
-        self.path = self.PATH_BASE + str(index)
-        self.bus = bus
-        self.ad_type = advertising_type
-        self.service_uuids = None
-        self.manufacturer_data = None
-        self.solicit_uuids = None
-        self.service_data = None
-        dbus.service.Object.__init__(self, bus, self.path)
-
-    def get_properties(self):
-        properties = dict()
-        properties['Type'] = self.ad_type
-        if self.service_uuids is not None:
-            properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
-                                                    signature='s')
-        if self.solicit_uuids is not None:
-            properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
-                                                    signature='s')
-        if self.manufacturer_data is not None:
-            properties['ManufacturerData'] = dbus.Dictionary(
-                self.manufacturer_data, signature='qay')
-        if self.service_data is not None:
-            properties['ServiceData'] = dbus.Dictionary(self.service_data,
-                                                        signature='say')
-        return {LE_ADVERTISEMENT_IFACE: properties}
-
-    def get_path(self):
-        return dbus.ObjectPath(self.path)
-
-    def add_service_uuid(self, uuid):
-        if not self.service_uuids:
-            self.service_uuids = []
-        self.service_uuids.append(uuid)
-
-    def add_solicit_uuid(self, uuid):
-        if not self.solicit_uuids:
-            self.solicit_uuids = []
-        self.solicit_uuids.append(uuid)
-
-    def add_manufacturer_data(self, manuf_code, data):
-        if not self.manufacturer_data:
-            self.manufacturer_data = dict()
-        self.manufacturer_data[manuf_code] = data
-
-    def add_service_data(self, uuid, data):
-        if not self.service_data:
-            self.service_data = dict()
-        self.service_data[uuid] = data
-
-    @dbus.service.method(DBUS_PROP_IFACE,
-                         in_signature='s',
-                         out_signature='a{sv}')
-    def GetAll(self, interface):
-        print 'GetAll'
-        if interface != LE_ADVERTISEMENT_IFACE:
-            raise InvalidArgsException()
-        print 'returning props'
-        return self.get_properties()[LE_ADVERTISEMENT_IFACE]
-
-    @dbus.service.method(LE_ADVERTISEMENT_IFACE,
-                         in_signature='',
-                         out_signature='')
-    def Release(self):
-        print '%s: Released!' % self.path
-
-class TestAdvertisement(Advertisement):
-
-    def __init__(self, bus, index):
-        Advertisement.__init__(self, bus, index, 'broadcast')
-        self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
-        self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
-        self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
-        self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
-                              [0x00, 0x01, 0x02, 0x03, 0x04])
-
-
-def register_ad_cb():
-    print 'Advertisement registered'
-
-
-def register_ad_error_cb(error):
-    print 'Failed to register advertisement: ' + str(error)
-    mainloop.quit()
-
-
-def find_adapter(bus):
-    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
-                               DBUS_OM_IFACE)
-    objects = remote_om.GetManagedObjects()
-
-    for o, props in objects.iteritems():
-        if LE_ADVERTISING_MANAGER_IFACE in props:
-            return o
-
-    return None
-
-
-def main():
-    global mainloop
-
-    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
-    bus = dbus.SystemBus()
-
-    adapter = find_adapter(bus)
-    if not adapter:
-        print 'LEAdvertisingManager1 interface not found'
-        return
-
-    ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
-                                LE_ADVERTISING_MANAGER_IFACE)
-
-    test_advertisement = TestAdvertisement(bus, 0)
-
-    mainloop = gobject.MainLoop()
-
-    ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
-                                     reply_handler=register_ad_cb,
-                                     error_handler=register_ad_error_cb)
-
-    mainloop.run()
-
-if __name__ == '__main__':
-    main()
index 7da0c7b..26c3578 100755 (executable)
@@ -51,6 +51,7 @@ class Advertisement(dbus.service.Object):
         self.manufacturer_data = None
         self.solicit_uuids = None
         self.service_data = None
+        self.local_name = None
         self.include_tx_power = None
         dbus.service.Object.__init__(self, bus, self.path)
 
@@ -69,6 +70,8 @@ class Advertisement(dbus.service.Object):
         if self.service_data is not None:
             properties['ServiceData'] = dbus.Dictionary(self.service_data,
                                                         signature='sv')
+        if self.local_name is not None:
+            properties['LocalName'] = dbus.String(self.local_name)
         if self.include_tx_power is not None:
             properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
         return {LE_ADVERTISEMENT_IFACE: properties}
@@ -96,6 +99,11 @@ class Advertisement(dbus.service.Object):
             self.service_data = dbus.Dictionary({}, signature='sv')
         self.service_data[uuid] = dbus.Array(data, signature='y')
 
+    def add_local_name(self, name):
+        if not self.local_name:
+            self.local_name = ""
+        self.local_name = dbus.String(name)
+
     @dbus.service.method(DBUS_PROP_IFACE,
                          in_signature='s',
                          out_signature='a{sv}')
@@ -120,6 +128,7 @@ class TestAdvertisement(Advertisement):
         self.add_service_uuid('180F')
         self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
         self.add_service_data('9999', [0x00, 0x01, 0x02, 0x03, 0x04])
+        self.add_local_name('TestAdvertisement')
         self.include_tx_power = True
 
 
index 24aaff9..689e86f 100755 (executable)
@@ -401,6 +401,8 @@ class BatteryLevelCharacteristic(Characteristic):
                 { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
 
     def drain_battery(self):
+        if not self.notifying:
+            return True
         if self.battery_lvl > 0:
             self.battery_lvl -= 2
             if self.battery_lvl < 0:
diff --git a/tools/advtest.c b/tools/advtest.c
new file mode 100644 (file)
index 0000000..b02301c
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <getopt.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hci.h"
+#include "src/shared/crypto.h"
+
+#define PEER_ADDR_TYPE 0x00
+#define PEER_ADDR      "\x00\x00\x00\x00\x00\x00"
+
+#define ADV_IRK                "\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+                       "\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
+#define SCAN_IRK       "\xfa\x73\x09\x11\x3f\x03\x37\x0f" \
+                       "\xf4\xf9\x93\x1e\xf9\xa3\x63\xa6"
+
+static struct mgmt *mgmt;
+static uint16_t index1 = MGMT_INDEX_NONE;
+static uint16_t index2 = MGMT_INDEX_NONE;
+
+static struct bt_crypto *crypto;
+static struct bt_hci *adv_dev;
+static struct bt_hci *scan_dev;
+
+static void print_rpa(const uint8_t addr[6])
+{
+       printf("  Address:  %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                       addr[5], addr[4], addr[3],
+                                       addr[2], addr[1], addr[0]);
+       printf("    Random: %02x%02x%02x\n", addr[3], addr[4], addr[5]);
+       printf("    Hash:   %02x%02x%02x\n", addr[0], addr[1], addr[2]);
+}
+
+static void scan_le_adv_report(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_evt_le_adv_report *evt = data;
+
+       if (evt->addr_type == 0x01 && (evt->addr[5] & 0xc0) == 0x40) {
+               uint8_t hash[3], irk[16];
+
+               memcpy(irk, ADV_IRK, 16);
+               bt_crypto_ah(crypto, irk, evt->addr + 3, hash);
+
+               if (!memcmp(evt->addr, hash, 3)) {
+                       printf("Received advertising report\n");
+                       print_rpa(evt->addr);
+
+                       memcpy(irk, ADV_IRK, 16);
+                       bt_crypto_ah(crypto, irk, evt->addr + 3, hash);
+
+                       printf("  -> Computed hash: %02x%02x%02x\n",
+                                               hash[0], hash[1], hash[2]);
+
+                       mainloop_quit();
+               }
+       }
+}
+
+static void scan_le_meta_event(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t evt_code = ((const uint8_t *) data)[0];
+
+       switch (evt_code) {
+       case BT_HCI_EVT_LE_ADV_REPORT:
+               scan_le_adv_report(data + 1, size - 1, user_data);
+               break;
+       }
+}
+
+static void scan_enable_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+}
+
+static void adv_enable_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct bt_hci_cmd_le_set_scan_parameters cmd4;
+       struct bt_hci_cmd_le_set_scan_enable cmd5;
+
+       cmd4.type = 0x00;               /* Passive scanning */
+       cmd4.interval = cpu_to_le16(0x0010);
+       cmd4.window = cpu_to_le16(0x0010);
+       cmd4.own_addr_type = 0x00;      /* Use public address */
+       cmd4.filter_policy = 0x00;
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS,
+                                       &cmd4, sizeof(cmd4), NULL, NULL, NULL);
+
+       cmd5.enable = 0x01;
+       cmd5.filter_dup = 0x01;
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+                                       &cmd5, sizeof(cmd5),
+                                       scan_enable_callback, NULL, NULL);
+}
+
+static void adv_le_evtmask_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct bt_hci_cmd_le_set_resolv_timeout cmd0;
+       struct bt_hci_cmd_le_add_to_resolv_list cmd1;
+       struct bt_hci_cmd_le_set_resolv_enable cmd2;
+       struct bt_hci_cmd_le_set_random_address cmd3;
+       struct bt_hci_cmd_le_set_adv_parameters cmd4;
+       struct bt_hci_cmd_le_set_adv_enable cmd5;
+
+       cmd0.timeout = cpu_to_le16(0x0384);
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_TIMEOUT,
+                                       &cmd0, sizeof(cmd0), NULL, NULL, NULL);
+
+       cmd1.addr_type = PEER_ADDR_TYPE;
+       memcpy(cmd1.addr, PEER_ADDR, 6);
+       memset(cmd1.peer_irk, 0, 16);
+       memcpy(cmd1.local_irk, ADV_IRK, 16);
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_ADD_TO_RESOLV_LIST,
+                                       &cmd1, sizeof(cmd1), NULL, NULL, NULL);
+
+       cmd2.enable = 0x01;
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RESOLV_ENABLE,
+                                       &cmd2, sizeof(cmd2), NULL, NULL, NULL);
+
+       bt_crypto_random_bytes(crypto, cmd3.addr + 3, 3);
+       cmd3.addr[5] &= 0x3f;   /* Clear two most significant bits */
+       cmd3.addr[5] |= 0x40;   /* Set second most significant bit */
+       bt_crypto_ah(crypto, cmd1.local_irk, cmd3.addr + 3, cmd3.addr);
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+                                       &cmd3, sizeof(cmd3), NULL, NULL, NULL);
+
+       printf("Setting advertising address\n");
+       print_rpa(cmd3.addr);
+
+       cmd4.min_interval = cpu_to_le16(0x0800);
+       cmd4.max_interval = cpu_to_le16(0x0800);
+       cmd4.type = 0x03;               /* Non-connectable advertising */
+       cmd4.own_addr_type = 0x03;      /* Local IRK, random address fallback */
+       cmd4.direct_addr_type = PEER_ADDR_TYPE;
+       memcpy(cmd4.direct_addr, PEER_ADDR, 6);
+       cmd4.channel_map = 0x07;
+       cmd4.filter_policy = 0x00;
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                                       &cmd4, sizeof(cmd4), NULL, NULL, NULL);
+
+       cmd5.enable = 0x01;
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                       &cmd5, sizeof(cmd5),
+                                       adv_enable_callback, NULL, NULL);
+}
+
+static void adv_le_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_le_read_local_features *rsp = data;
+       uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local LE features\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8,
+                                       adv_le_evtmask_callback, NULL, NULL);
+}
+
+static void adv_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_features *rsp = data;
+       uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 };
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local features\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       if (!(rsp->features[4] & 0x40)) {
+               fprintf(stderr, "Controller without Low Energy support\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8,
+                                                       NULL, NULL, NULL);
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0,
+                                       adv_le_features_callback, NULL, NULL);
+}
+
+static void scan_le_evtmask_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       bt_hci_send(adv_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+                                       adv_features_callback, NULL, NULL);
+}
+
+static void scan_le_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_le_read_local_features *rsp = data;
+       uint8_t evtmask[] = { 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local LE features\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       bt_hci_send(adv_dev, BT_HCI_CMD_LE_SET_EVENT_MASK, evtmask, 8,
+                                       scan_le_evtmask_callback, NULL, NULL);
+}
+
+static void scan_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_features *rsp = data;
+       uint8_t evtmask[] = { 0x90, 0xe8, 0x04, 0x02, 0x00, 0x80, 0x00, 0x20 };
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local features\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       if (!(rsp->features[4] & 0x40)) {
+               fprintf(stderr, "Controller without Low Energy support\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask, 8,
+                                                       NULL, NULL, NULL);
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_LE_READ_LOCAL_FEATURES, NULL, 0,
+                                       scan_le_features_callback, NULL, NULL);
+}
+
+static void read_index_list(uint8_t status, uint16_t len, const void *param,
+                                                       void *user_data)
+{
+       const struct mgmt_rp_read_index_list *rp = param;
+       uint16_t count;
+       int i;
+
+       if (status) {
+               fprintf(stderr, "Reading index list failed: %s\n",
+                                               mgmt_errstr(status));
+               mainloop_exit_failure();
+               return;
+       }
+
+       count = le16_to_cpu(rp->num_controllers);
+
+       if (count < 2) {
+               fprintf(stderr, "At least 2 controllers are required\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       for (i = 0; i < count; i++) {
+               uint16_t index = cpu_to_le16(rp->index[i]);
+
+               if (index < index1)
+                       index1 = index;
+       }
+
+       for (i = 0; i < count; i++) {
+               uint16_t index = cpu_to_le16(rp->index[i]);
+
+               if (index < index2 && index > index1)
+                       index2 = index;
+       }
+
+       printf("Selecting index %u for advertiser\n", index1);
+       printf("Selecting index %u for scanner\n", index2);
+
+       crypto = bt_crypto_new();
+       if (!crypto) {
+               fprintf(stderr, "Failed to open crypto interface\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       adv_dev = bt_hci_new_user_channel(index1);
+       if (!adv_dev) {
+               fprintf(stderr, "Failed to open HCI for advertiser\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       scan_dev = bt_hci_new_user_channel(index2);
+       if (!scan_dev) {
+               fprintf(stderr, "Failed to open HCI for scanner\n");
+               mainloop_exit_failure();
+               return;
+       }
+
+       bt_hci_register(scan_dev, BT_HCI_EVT_LE_META_EVENT,
+                                       scan_le_meta_event, NULL, NULL);
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+       bt_hci_send(scan_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+                                       scan_features_callback, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("advtest - Advertising testing\n"
+               "Usage:\n");
+       printf("\tadvtest [options]\n");
+       printf("options:\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "version",   no_argument,       NULL, 'v' },
+       { "help",      no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc ,char *argv[])
+{
+       sigset_t mask;
+       int exit_status;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "vh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       mgmt = mgmt_new_default();
+       if (!mgmt) {
+               fprintf(stderr, "Failed to open management socket\n");
+               return EXIT_FAILURE;
+       }
+
+       if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+                                       MGMT_INDEX_NONE, 0, NULL,
+                                       read_index_list, NULL, NULL)) {
+               fprintf(stderr, "Failed to read index list\n");
+               exit_status = EXIT_FAILURE;
+               goto done;
+       }
+
+       exit_status = mainloop_run();
+
+       bt_hci_unref(adv_dev);
+       bt_hci_unref(scan_dev);
+
+       bt_crypto_unref(crypto);
+
+done:
+       mgmt_unref(mgmt);
+
+       return exit_status;
+}
index 70c9b65..59fb1da 100755 (executable)
@@ -799,7 +799,7 @@ int main(int argc, char *argv[])
 
                case 'f':
                        invalid = 1;
-                       /* Intentionally missing break */
+                       /* fall through */
 
                case 's':
                        mode = MODE_SEND;
index 5fc6269..6051f30 100755 (executable)
@@ -621,6 +621,7 @@ static const struct {
        { 0x0a, "iBT 2.1 (AG620)"       },
        { 0x0b, "iBT 3.0 (LnP)"         },
        { 0x0c, "iBT 3.0 (WsP)"         },
+       { 0x12, "iBT 3.5 (ThP)"         },
        { }
 };
 
index 9e19997..b22de27 100755 (executable)
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <signal.h>
-#include <sys/signalfd.h>
+#include <string.h>
 
-#include <readline/readline.h>
-#include <readline/history.h>
 #include <glib.h>
 
 #include "gdbus/gdbus.h"
-#include "client/display.h"
+#include "src/shared/shell.h"
 
 /* String display constants */
 #define COLORED_NEW    COLOR_GREEN "NEW" COLOR_OFF
@@ -52,7 +49,6 @@
 #define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
 #define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
 
-static GMainLoop *main_loop;
 static DBusConnection *dbus_conn;
 static GDBusProxy *default_player;
 static GSList *players = NULL;
@@ -61,29 +57,18 @@ static GSList *items = NULL;
 
 static void connect_handler(DBusConnection *connection, void *user_data)
 {
-       rl_set_prompt(PROMPT_ON);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
+       bt_shell_set_prompt(PROMPT_ON);
 }
 
 static void disconnect_handler(DBusConnection *connection, void *user_data)
 {
-       rl_set_prompt(PROMPT_OFF);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
-}
-
-static void cmd_quit(int argc, char *argv[])
-{
-       g_main_loop_quit(main_loop);
+       bt_shell_set_prompt(PROMPT_OFF);
 }
 
 static bool check_default_player(void)
 {
        if (!default_player) {
-               rl_printf("No default player available\n");
+               bt_shell_printf("No default player available\n");
                return FALSE;
        }
 
@@ -97,12 +82,12 @@ static void play_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to play: %s\n", error.name);
+               bt_shell_printf("Failed to play: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Play successful\n");
+       bt_shell_printf("Play successful\n");
 }
 
 static GDBusProxy *find_item(const char *path)
@@ -125,17 +110,17 @@ static void cmd_play_item(int argc, char *argv[])
 
        proxy = find_item(argv[1]);
        if (proxy == NULL) {
-               rl_printf("Item %s not available\n", argv[1]);
+               bt_shell_printf("Item %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "Play", NULL, play_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to play\n");
+               bt_shell_printf("Failed to play\n");
                return;
        }
 
-       rl_printf("Attempting to play %s\n", argv[1]);
+       bt_shell_printf("Attempting to play %s\n", argv[1]);
 }
 
 static void cmd_play(int argc, char *argv[])
@@ -148,11 +133,11 @@ static void cmd_play(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Play", NULL, play_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to play\n");
+               bt_shell_printf("Failed to play\n");
                return;
        }
 
-       rl_printf("Attempting to play\n");
+       bt_shell_printf("Attempting to play\n");
 }
 
 static void pause_reply(DBusMessage *message, void *user_data)
@@ -162,12 +147,12 @@ static void pause_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to pause: %s\n", error.name);
+               bt_shell_printf("Failed to pause: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Pause successful\n");
+       bt_shell_printf("Pause successful\n");
 }
 
 static void cmd_pause(int argc, char *argv[])
@@ -177,11 +162,11 @@ static void cmd_pause(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Pause", NULL,
                                        pause_reply, NULL, NULL) == FALSE) {
-               rl_printf("Failed to play\n");
+               bt_shell_printf("Failed to play\n");
                return;
        }
 
-       rl_printf("Attempting to pause\n");
+       bt_shell_printf("Attempting to pause\n");
 }
 
 static void stop_reply(DBusMessage *message, void *user_data)
@@ -191,12 +176,12 @@ static void stop_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to stop: %s\n", error.name);
+               bt_shell_printf("Failed to stop: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Stop successful\n");
+       bt_shell_printf("Stop successful\n");
 }
 
 static void cmd_stop(int argc, char *argv[])
@@ -206,11 +191,11 @@ static void cmd_stop(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Stop", NULL, stop_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to stop\n");
+               bt_shell_printf("Failed to stop\n");
                return;
        }
 
-       rl_printf("Attempting to stop\n");
+       bt_shell_printf("Attempting to stop\n");
 }
 
 static void next_reply(DBusMessage *message, void *user_data)
@@ -220,12 +205,12 @@ static void next_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to jump to next: %s\n", error.name);
+               bt_shell_printf("Failed to jump to next: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Next successful\n");
+       bt_shell_printf("Next successful\n");
 }
 
 static void cmd_next(int argc, char *argv[])
@@ -235,11 +220,11 @@ static void cmd_next(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Next", NULL, next_reply,
                                                        NULL, NULL) == FALSE) {
-               rl_printf("Failed to jump to next\n");
+               bt_shell_printf("Failed to jump to next\n");
                return;
        }
 
-       rl_printf("Attempting to jump to next\n");
+       bt_shell_printf("Attempting to jump to next\n");
 }
 
 static void previous_reply(DBusMessage *message, void *user_data)
@@ -249,12 +234,12 @@ static void previous_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to jump to previous: %s\n", error.name);
+               bt_shell_printf("Failed to jump to previous: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Previous successful\n");
+       bt_shell_printf("Previous successful\n");
 }
 
 static void cmd_previous(int argc, char *argv[])
@@ -264,11 +249,11 @@ static void cmd_previous(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Previous", NULL,
                                        previous_reply, NULL, NULL) == FALSE) {
-               rl_printf("Failed to jump to previous\n");
+               bt_shell_printf("Failed to jump to previous\n");
                return;
        }
 
-       rl_printf("Attempting to jump to previous\n");
+       bt_shell_printf("Attempting to jump to previous\n");
 }
 
 static void fast_forward_reply(DBusMessage *message, void *user_data)
@@ -278,12 +263,12 @@ static void fast_forward_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to fast forward: %s\n", error.name);
+               bt_shell_printf("Failed to fast forward: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("FastForward successful\n");
+       bt_shell_printf("FastForward successful\n");
 }
 
 static void cmd_fast_forward(int argc, char *argv[])
@@ -293,11 +278,11 @@ static void cmd_fast_forward(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "FastForward", NULL,
                                fast_forward_reply, NULL, NULL) == FALSE) {
-               rl_printf("Failed to jump to previous\n");
+               bt_shell_printf("Failed to jump to previous\n");
                return;
        }
 
-       rl_printf("Fast forward playback\n");
+       bt_shell_printf("Fast forward playback\n");
 }
 
 static void rewind_reply(DBusMessage *message, void *user_data)
@@ -307,12 +292,12 @@ static void rewind_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to rewind: %s\n", error.name);
+               bt_shell_printf("Failed to rewind: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Rewind successful\n");
+       bt_shell_printf("Rewind successful\n");
 }
 
 static void cmd_rewind(int argc, char *argv[])
@@ -322,11 +307,11 @@ static void cmd_rewind(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(default_player, "Rewind", NULL,
                                        rewind_reply, NULL, NULL) == FALSE) {
-               rl_printf("Failed to rewind\n");
+               bt_shell_printf("Failed to rewind\n");
                return;
        }
 
-       rl_printf("Rewind playback\n");
+       bt_shell_printf("Rewind playback\n");
 }
 
 static void generic_callback(const DBusError *error, void *user_data)
@@ -334,9 +319,9 @@ static void generic_callback(const DBusError *error, void *user_data)
        char *str = user_data;
 
        if (dbus_error_is_set(error))
-               rl_printf("Failed to set %s: %s\n", str, error->name);
+               bt_shell_printf("Failed to set %s: %s\n", str, error->name);
        else
-               rl_printf("Changing %s succeeded\n", str);
+               bt_shell_printf("Changing %s succeeded\n", str);
 }
 
 static void cmd_equalizer(int argc, char *argv[])
@@ -347,13 +332,8 @@ static void cmd_equalizer(int argc, char *argv[])
        if (!check_default_player())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing on/off argument\n");
-               return;
-       }
-
        if (!g_dbus_proxy_get_property(default_player, "Equalizer", &iter)) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -363,12 +343,12 @@ static void cmd_equalizer(int argc, char *argv[])
                                                DBUS_TYPE_STRING, &value,
                                                generic_callback, value,
                                                g_free) == FALSE) {
-               rl_printf("Failed to setting equalizer\n");
+               bt_shell_printf("Failed to setting equalizer\n");
                g_free(value);
                return;
        }
 
-       rl_printf("Attempting to set equalizer\n");
+       bt_shell_printf("Attempting to set equalizer\n");
 }
 
 static void cmd_repeat(int argc, char *argv[])
@@ -379,13 +359,9 @@ static void cmd_repeat(int argc, char *argv[])
        if (!check_default_player())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing mode argument\n");
-               return;
-       }
 
        if (!g_dbus_proxy_get_property(default_player, "Repeat", &iter)) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -395,12 +371,12 @@ static void cmd_repeat(int argc, char *argv[])
                                                DBUS_TYPE_STRING, &value,
                                                generic_callback, value,
                                                g_free) == FALSE) {
-               rl_printf("Failed to set repeat\n");
+               bt_shell_printf("Failed to set repeat\n");
                g_free(value);
                return;
        }
 
-       rl_printf("Attempting to set repeat\n");
+       bt_shell_printf("Attempting to set repeat\n");
 }
 
 static void cmd_shuffle(int argc, char *argv[])
@@ -411,13 +387,8 @@ static void cmd_shuffle(int argc, char *argv[])
        if (!check_default_player())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing mode argument\n");
-               return;
-       }
-
        if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -427,12 +398,12 @@ static void cmd_shuffle(int argc, char *argv[])
                                                DBUS_TYPE_STRING, &value,
                                                generic_callback, value,
                                                g_free) == FALSE) {
-               rl_printf("Failed to set shuffle\n");
+               bt_shell_printf("Failed to set shuffle\n");
                g_free(value);
                return;
        }
 
-       rl_printf("Attempting to set shuffle\n");
+       bt_shell_printf("Attempting to set shuffle\n");
 }
 
 static void cmd_scan(int argc, char *argv[])
@@ -443,13 +414,8 @@ static void cmd_scan(int argc, char *argv[])
        if (!check_default_player())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing mode argument\n");
-               return;
-       }
-
        if (!g_dbus_proxy_get_property(default_player, "Shuffle", &iter)) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -459,12 +425,12 @@ static void cmd_scan(int argc, char *argv[])
                                                DBUS_TYPE_STRING, &value,
                                                generic_callback, value,
                                                g_free) == FALSE) {
-               rl_printf("Failed to set scan\n");
+               bt_shell_printf("Failed to set scan\n");
                g_free(value);
                return;
        }
 
-       rl_printf("Attempting to set scan\n");
+       bt_shell_printf("Attempting to set scan\n");
 }
 
 static char *proxy_description(GDBusProxy *proxy, const char *title,
@@ -487,7 +453,7 @@ static void print_player(GDBusProxy *proxy, const char *description)
 
        str = proxy_description(proxy, "Player", description);
 
-       rl_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
+       bt_shell_printf("%s%s\n", str, default_player == proxy ? "[default]" : "");
 
        g_free(str);
 }
@@ -527,35 +493,35 @@ static void print_iter(const char *label, const char *name,
        DBusMessageIter subiter;
 
        if (iter == NULL) {
-               rl_printf("%s%s is nil\n", label, name);
+               bt_shell_printf("%s%s is nil\n", label, name);
                return;
        }
 
        switch (dbus_message_iter_get_arg_type(iter)) {
        case DBUS_TYPE_INVALID:
-               rl_printf("%s%s is invalid\n", label, name);
+               bt_shell_printf("%s%s is invalid\n", label, name);
                break;
        case DBUS_TYPE_STRING:
        case DBUS_TYPE_OBJECT_PATH:
                dbus_message_iter_get_basic(iter, &valstr);
-               rl_printf("%s%s: %s\n", label, name, valstr);
+               bt_shell_printf("%s%s: %s\n", label, name, valstr);
                break;
        case DBUS_TYPE_BOOLEAN:
                dbus_message_iter_get_basic(iter, &valbool);
-               rl_printf("%s%s: %s\n", label, name,
+               bt_shell_printf("%s%s: %s\n", label, name,
                                        valbool == TRUE ? "yes" : "no");
                break;
        case DBUS_TYPE_UINT32:
                dbus_message_iter_get_basic(iter, &valu32);
-               rl_printf("%s%s: 0x%06x\n", label, name, valu32);
+               bt_shell_printf("%s%s: 0x%06x\n", label, name, valu32);
                break;
        case DBUS_TYPE_UINT16:
                dbus_message_iter_get_basic(iter, &valu16);
-               rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+               bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
                break;
        case DBUS_TYPE_INT16:
                dbus_message_iter_get_basic(iter, &vals16);
-               rl_printf("%s%s: %d\n", label, name, vals16);
+               bt_shell_printf("%s%s: %d\n", label, name, vals16);
                break;
        case DBUS_TYPE_VARIANT:
                dbus_message_iter_recurse(iter, &subiter);
@@ -576,7 +542,7 @@ static void print_iter(const char *label, const char *name,
                print_iter(label, valstr, &subiter);
                break;
        default:
-               rl_printf("%s%s has unsupported type\n", label, name);
+               bt_shell_printf("%s%s has unsupported type\n", label, name);
                break;
        }
 }
@@ -609,18 +575,13 @@ static void cmd_show_item(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing item address argument\n");
-               return;
-       }
-
        proxy = find_item(argv[1]);
        if (!proxy) {
-               rl_printf("Item %s not available\n", argv[1]);
+               bt_shell_printf("Item %s not available\n", argv[1]);
                return;
        }
 
-       rl_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Item %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(proxy, "Player");
        print_property(proxy, "Name");
@@ -646,12 +607,12 @@ static void cmd_show(int argc, char *argv[])
        } else {
                proxy = find_player(argv[1]);
                if (!proxy) {
-                       rl_printf("Player %s not available\n", argv[1]);
+                       bt_shell_printf("Player %s not available\n", argv[1]);
                        return;
                }
        }
 
-       rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(proxy, "Name");
        print_property(proxy, "Repeat");
@@ -666,7 +627,7 @@ static void cmd_show(int argc, char *argv[])
        if (folder == NULL)
                return;
 
-       rl_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Folder %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(folder, "Name");
        print_property(folder, "NumberOfItems");
@@ -680,7 +641,7 @@ static void cmd_show(int argc, char *argv[])
        if (item == NULL)
                return;
 
-       rl_printf("Playlist %s\n", path);
+       bt_shell_printf("Playlist %s\n", path);
 
        print_property(item, "Name");
 }
@@ -689,14 +650,9 @@ static void cmd_select(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing player address argument\n");
-               return;
-       }
-
        proxy = find_player(argv[1]);
        if (proxy == NULL) {
-               rl_printf("Player %s not available\n", argv[1]);
+               bt_shell_printf("Player %s not available\n", argv[1]);
                return;
        }
 
@@ -714,12 +670,12 @@ static void change_folder_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to change folder: %s\n", error.name);
+               bt_shell_printf("Failed to change folder: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("ChangeFolder successful\n");
+       bt_shell_printf("ChangeFolder successful\n");
 }
 
 static void change_folder_setup(DBusMessageIter *iter, void *user_data)
@@ -733,13 +689,8 @@ static void cmd_change_folder(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing item argument\n");
-               return;
-       }
-
        if (dbus_validate_path(argv[1], NULL) == FALSE) {
-               rl_printf("Not a valid path\n");
+               bt_shell_printf("Not a valid path\n");
                return;
        }
 
@@ -748,17 +699,17 @@ static void cmd_change_folder(int argc, char *argv[])
 
        proxy = find_folder(g_dbus_proxy_get_path(default_player));
        if (proxy == NULL) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
                                change_folder_reply, argv[1], NULL) == FALSE) {
-               rl_printf("Failed to change current folder\n");
+               bt_shell_printf("Failed to change current folder\n");
                return;
        }
 
-       rl_printf("Attempting to change folder\n");
+       bt_shell_printf("Attempting to change folder\n");
 }
 
 static void append_variant(DBusMessageIter *iter, int type, void *val)
@@ -832,12 +783,12 @@ static void list_items_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to list items: %s\n", error.name);
+               bt_shell_printf("Failed to list items: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("ListItems successful\n");
+       bt_shell_printf("ListItems successful\n");
 }
 
 static void cmd_list_items(int argc, char *argv[])
@@ -850,7 +801,7 @@ static void cmd_list_items(int argc, char *argv[])
 
        proxy = find_folder(g_dbus_proxy_get_path(default_player));
        if (proxy == NULL) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -864,7 +815,7 @@ static void cmd_list_items(int argc, char *argv[])
        errno = 0;
        args->start = strtol(argv[1], NULL, 10);
        if (errno != 0) {
-               rl_printf("%s(%d)\n", strerror(errno), errno);
+               bt_shell_printf("%s(%d)\n", strerror(errno), errno);
                g_free(args);
                return;
        }
@@ -875,7 +826,7 @@ static void cmd_list_items(int argc, char *argv[])
        errno = 0;
        args->end = strtol(argv[2], NULL, 10);
        if (errno != 0) {
-               rl_printf("%s(%d)\n", strerror(errno), errno);
+               bt_shell_printf("%s(%d)\n", strerror(errno), errno);
                g_free(args);
                return;
        }
@@ -883,12 +834,12 @@ static void cmd_list_items(int argc, char *argv[])
 done:
        if (g_dbus_proxy_method_call(proxy, "ListItems", list_items_setup,
                                list_items_reply, args, g_free) == FALSE) {
-               rl_printf("Failed to change current folder\n");
+               bt_shell_printf("Failed to change current folder\n");
                g_free(args);
                return;
        }
 
-       rl_printf("Attempting to list items\n");
+       bt_shell_printf("Attempting to list items\n");
 }
 
 static void search_setup(DBusMessageIter *iter, void *user_data)
@@ -915,12 +866,12 @@ static void search_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to search: %s\n", error.name);
+               bt_shell_printf("Failed to search: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Search successful\n");
+       bt_shell_printf("Search successful\n");
 }
 
 static void cmd_search(int argc, char *argv[])
@@ -928,17 +879,12 @@ static void cmd_search(int argc, char *argv[])
        GDBusProxy *proxy;
        char *string;
 
-       if (argc < 2) {
-               rl_printf("Missing string argument\n");
-               return;
-       }
-
        if (check_default_player() == FALSE)
                return;
 
        proxy = find_folder(g_dbus_proxy_get_path(default_player));
        if (proxy == NULL) {
-               rl_printf("Operation not supported\n");
+               bt_shell_printf("Operation not supported\n");
                return;
        }
 
@@ -946,12 +892,12 @@ static void cmd_search(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
                                search_reply, string, g_free) == FALSE) {
-               rl_printf("Failed to search\n");
+               bt_shell_printf("Failed to search\n");
                g_free(string);
                return;
        }
 
-       rl_printf("Attempting to search\n");
+       bt_shell_printf("Attempting to search\n");
 }
 
 static void add_to_nowplaying_reply(DBusMessage *message, void *user_data)
@@ -961,45 +907,37 @@ static void add_to_nowplaying_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to queue: %s\n", error.name);
+               bt_shell_printf("Failed to queue: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("AddToNowPlaying successful\n");
+       bt_shell_printf("AddToNowPlaying successful\n");
 }
 
 static void cmd_queue(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing item address argument\n");
-               return;
-       }
-
        proxy = find_item(argv[1]);
        if (proxy == NULL) {
-               rl_printf("Item %s not available\n", argv[1]);
+               bt_shell_printf("Item %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "AddtoNowPlaying", NULL,
                                        add_to_nowplaying_reply, NULL,
                                        NULL) == FALSE) {
-               rl_printf("Failed to play\n");
+               bt_shell_printf("Failed to play\n");
                return;
        }
 
-       rl_printf("Attempting to queue %s\n", argv[1]);
+       bt_shell_printf("Attempting to queue %s\n", argv[1]);
 }
 
-static const struct {
-       const char *cmd;
-       const char *arg;
-       void (*func) (int argc, char *argv[]);
-       const char *desc;
-} cmd_table[] = {
+static const struct bt_shell_menu main_menu = {
+       .name = "main",
+       .entries = {
        { "list",         NULL,       cmd_list, "List available players" },
        { "show",         "[player]", cmd_show, "Player information" },
        { "select",       "<player>", cmd_select, "Select default player" },
@@ -1023,223 +961,13 @@ static const struct {
                                                "Change current folder" },
        { "list-items", "[start] [end]",  cmd_list_items,
                                        "List items of current folder" },
-       { "search",     "string",     cmd_search,
+       { "search",     "<string>",   cmd_search,
                                        "Search items containing string" },
        { "queue",       "<item>",    cmd_queue, "Add item to playlist queue" },
        { "show-item",   "<item>",    cmd_show_item, "Show item information" },
-       { "quit",         NULL,       cmd_quit, "Quit program" },
-       { "exit",         NULL,       cmd_quit },
-       { "help" },
-       {}
-};
-
-static char *cmd_generator(const char *text, int state)
-{
-       static int index, len;
-       const char *cmd;
-
-       if (!state) {
-               index = 0;
-               len = strlen(text);
-       }
-
-       while ((cmd = cmd_table[index].cmd)) {
-               index++;
-
-               if (!strncmp(cmd, text, len))
-                       return strdup(cmd);
-       }
-
-       return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
-       char **matches = NULL;
-
-       if (start == 0) {
-               rl_completion_display_matches_hook = NULL;
-               matches = rl_completion_matches(text, cmd_generator);
-       }
-
-       if (!matches)
-               rl_attempted_completion_over = 1;
-
-       return matches;
-}
-
-static void rl_handler(char *input)
-{
-       int argc;
-       char **argv = NULL;
-       int i;
-
-       if (!input) {
-               rl_insert_text("quit");
-               rl_redisplay();
-               rl_crlf();
-               g_main_loop_quit(main_loop);
-               return;
-       }
-
-       if (!strlen(input))
-               goto done;
-
-       g_strstrip(input);
-       add_history(input);
-
-       argv = g_strsplit(input, " ", -1);
-       if (argv == NULL)
-               goto done;
-
-       for (argc = 0; argv[argc];)
-               argc++;
-
-       if (argc == 0)
-               goto done;
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (strcmp(argv[0], cmd_table[i].cmd))
-                       continue;
-
-               if (cmd_table[i].func) {
-                       cmd_table[i].func(argc, argv);
-                       goto done;
-               }
-       }
-
-       if (strcmp(argv[0], "help")) {
-               printf("Invalid command\n");
-               goto done;
-       }
-
-       printf("Available commands:\n");
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (cmd_table[i].desc)
-                       printf("\t%s %s\t%s\n", cmd_table[i].cmd,
-                                               cmd_table[i].arg ? : "    ",
-                                               cmd_table[i].desc);
-       }
-
-done:
-       g_strfreev(argv);
-       free(input);
-}
-
-static gboolean option_version = FALSE;
-
-static GOptionEntry options[] = {
-       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
-                               "Show version information and exit" },
-       { NULL },
+       {} },
 };
 
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
-{
-       static unsigned int __terminated = 0;
-       struct signalfd_siginfo si;
-       ssize_t result;
-       int fd;
-
-       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               g_main_loop_quit(main_loop);
-               return FALSE;
-       }
-
-       fd = g_io_channel_unix_get_fd(channel);
-
-       result = read(fd, &si, sizeof(si));
-       if (result != sizeof(si))
-               return FALSE;
-
-       switch (si.ssi_signo) {
-       case SIGINT:
-               rl_replace_line("", 0);
-               rl_crlf();
-               rl_on_new_line();
-               rl_redisplay();
-               break;
-       case SIGTERM:
-               if (__terminated == 0) {
-                       rl_replace_line("", 0);
-                       rl_crlf();
-                       g_main_loop_quit(main_loop);
-               }
-
-               __terminated = 1;
-               break;
-       }
-
-       return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
-       GIOChannel *channel;
-       guint source;
-       sigset_t mask;
-       int fd;
-
-       sigemptyset(&mask);
-       sigaddset(&mask, SIGINT);
-       sigaddset(&mask, SIGTERM);
-
-       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
-               perror("Failed to set signal mask");
-               return 0;
-       }
-
-       fd = signalfd(-1, &mask, 0);
-       if (fd < 0) {
-               perror("Failed to create signal descriptor");
-               return 0;
-       }
-
-       channel = g_io_channel_unix_new(fd);
-
-       g_io_channel_set_close_on_unref(channel, TRUE);
-       g_io_channel_set_encoding(channel, NULL, NULL);
-       g_io_channel_set_buffered(channel, FALSE);
-
-       source = g_io_add_watch(channel,
-                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               signal_handler, NULL);
-
-       g_io_channel_unref(channel);
-
-       return source;
-}
-
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
-{
-       if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
-               g_main_loop_quit(main_loop);
-               return FALSE;
-       }
-
-       rl_callback_read_char();
-       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 player_added(GDBusProxy *proxy)
 {
        players = g_slist_append(players, proxy);
@@ -1256,7 +984,7 @@ static void print_folder(GDBusProxy *proxy, const char *description)
 
        path = g_dbus_proxy_get_path(proxy);
 
-       rl_printf("%s%s%sFolder %s\n", description ? "[" : "",
+       bt_shell_printf("%s%s%sFolder %s\n", description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
                                        path);
@@ -1281,7 +1009,7 @@ static void print_item(GDBusProxy *proxy, const char *description)
        else
                name = "<unknown>";
 
-       rl_printf("%s%s%sItem %s %s\n", description ? "[" : "",
+       bt_shell_printf("%s%s%sItem %s %s\n", description ? "[" : "",
                                        description ? : "",
                                        description ? "] " : "",
                                        path, name);
@@ -1393,43 +1121,15 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 
 int main(int argc, char *argv[])
 {
-       GOptionContext *context;
-       GError *error = NULL;
        GDBusClient *client;
-       guint signal, input;
-
-       context = g_option_context_new(NULL);
-       g_option_context_add_main_entries(context, options, NULL);
-
-       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
-               if (error != NULL) {
-                       g_printerr("%s\n", error->message);
-                       g_error_free(error);
-               } else
-                       g_printerr("An unknown error occurred\n");
-               exit(1);
-       }
 
-       g_option_context_free(context);
-
-       if (option_version == TRUE) {
-               printf("%s\n", VERSION);
-               exit(0);
-       }
+       bt_shell_init(argc, argv, NULL);
+       bt_shell_set_menu(&main_menu);
+       bt_shell_set_prompt(PROMPT_OFF);
+       bt_shell_attach(fileno(stdin));
 
-       main_loop = g_main_loop_new(NULL, FALSE);
        dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
 
-       rl_attempted_completion_function = cmd_completion;
-
-       rl_erase_empty_line = 1;
-       rl_callback_handler_install(NULL, rl_handler);
-
-       rl_set_prompt(PROMPT_OFF);
-       rl_redisplay();
-
-       input = setup_standard_input();
-       signal = setup_signalfd();
        client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
 
        g_dbus_client_set_connect_watch(client, connect_handler, NULL);
@@ -1438,17 +1138,11 @@ int main(int argc, char *argv[])
        g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
                                                        property_changed, NULL);
 
-       g_main_loop_run(main_loop);
+       bt_shell_run();
 
        g_dbus_client_unref(client);
-       g_source_remove(signal);
-       g_source_remove(input);
-
-       rl_message("");
-       rl_callback_handler_remove();
 
        dbus_connection_unref(dbus_conn);
-       g_main_loop_unref(main_loop);
 
        return 0;
 }
diff --git a/tools/btconfig.c b/tools/btconfig.c
new file mode 100644 (file)
index 0000000..f171bfb
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+static struct mgmt *mgmt = NULL;
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       printf("%s%s\n", prefix, str);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       static bool terminated = false;
+
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               if (!terminated) {
+                       mainloop_quit();
+                       terminated = true;
+               }
+               break;
+       }
+}
+static void usage(void)
+{
+       printf("btconfig - Bluetooth configuration utility\n"
+               "Usage:\n");
+       printf("\tbtconfig [options]\n");
+       printf("options:\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "version",  no_argument,       NULL, 'v' },
+       { "help",     no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       sigset_t mask;
+       int exit_status;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "vh",
+                                               main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       mgmt = mgmt_new_default();
+       if (!mgmt) {
+               fprintf(stderr, "Unable to open mgmt_socket\n");
+               return EXIT_FAILURE;
+       }
+
+       if (getenv("MGMT_DEBUG"))
+               mgmt_set_debug(mgmt, mgmt_debug, "mgmt: ", NULL);
+
+       exit_status = mainloop_run();
+
+       mgmt_cancel_all(mgmt);
+       mgmt_unregister_all(mgmt);
+
+       mgmt_unref(mgmt);
+
+       return exit_status;
+}
index 31848f9..76853b8 100755 (executable)
@@ -235,7 +235,7 @@ static struct client *client_create(int fd, uint16_t mtu)
                                                                        NULL);
        }
 
-       bt_gatt_client_set_ready_handler(cli->gatt, ready_cb, cli, NULL);
+       bt_gatt_client_ready_register(cli->gatt, ready_cb, cli, NULL);
        bt_gatt_client_set_service_changed(cli->gatt, service_changed_cb, cli,
                                                                        NULL);
 
@@ -1129,7 +1129,7 @@ static void register_notify_cb(uint16_t att_ecode, void *user_data)
                return;
        }
 
-       PRLOG("Registered notify handler!");
+       PRLOG("Registered notify handler!\n");
 }
 
 static void cmd_register_notify(struct client *cli, char *cmd_str)
@@ -1164,7 +1164,7 @@ static void cmd_register_notify(struct client *cli, char *cmd_str)
                return;
        }
 
-       PRLOG("Registering notify handler with id: %u\n", id);
+       printf("Registering notify handler with id: %u\n", id);
 }
 
 static void unregister_notify_usage(void)
@@ -1205,16 +1205,15 @@ static void cmd_unregister_notify(struct client *cli, char *cmd_str)
 
 static void set_security_usage(void)
 {
-       printf("Usage: set_security <level>\n"
+       printf("Usage: set-security <level>\n"
                "level: 1-3\n"
                "e.g.:\n"
-               "\tset-sec-level 2\n");
+               "\tset-security 2\n");
 }
 
 static void cmd_set_security(struct client *cli, char *cmd_str)
 {
-       char *argvbuf[1];
-       char **argv = argvbuf;
+       char *argv[2];
        int argc = 0;
        char *endptr = NULL;
        int level;
index d5facc5..3911ba2 100755 (executable)
@@ -88,6 +88,17 @@ static int pending_index = 0;
 
 #define PROMPT_ON      COLOR_BLUE "[mgmt]" COLOR_OFF "# "
 
+static void set_index(char *arg)
+{
+       if (!arg || !strcmp(arg, "none") || !strcmp(arg, "any") ||
+                                               !strcmp(arg, "all"))
+               mgmt_index = MGMT_INDEX_NONE;
+       else if(strlen(arg) > 3 && !strncasecmp(arg, "hci", 3))
+               mgmt_index = atoi(&arg[3]);
+       else
+               mgmt_index = atoi(arg);
+}
+
 static void update_prompt(uint16_t index)
 {
        char str[32];
@@ -1129,6 +1140,8 @@ done:
        noninteractive_quit(EXIT_SUCCESS);
 }
 
+static void cmd_usage(char *cmd);
+
 static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc,
                                                                char **argv)
 {
@@ -1785,22 +1798,29 @@ done:
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
-                                                       int argc, char **argv)
+static bool parse_setting(int argc, char **argv, uint8_t *val)
 {
-       uint8_t val;
-
        if (argc < 2) {
-               print("Specify \"on\" or \"off\"");
-               return noninteractive_quit(EXIT_FAILURE);
+               cmd_usage(argv[0]);
+               return false;
        }
 
        if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
-               val = 1;
+               *val = 1;
        else if (strcasecmp(argv[1], "off") == 0)
-               val = 0;
+               *val = 0;
        else
-               val = atoi(argv[1]);
+               *val = atoi(argv[1]);
+       return true;
+}
+
+static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
+                                                       int argc, char **argv)
+{
+       uint8_t val;
+
+       if (parse_setting(argc, argv, &val) == false)
+               return noninteractive_quit(EXIT_FAILURE);
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -1822,7 +1842,7 @@ static void cmd_discov(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_cp_set_discoverable cp;
 
        if (argc < 2) {
-               print("Usage: %s <yes/no/limited> [timeout]", argv[0]);
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -1884,7 +1904,7 @@ static void cmd_sc(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        uint8_t val;
 
        if (argc < 2) {
-               print("Specify \"on\" or \"off\" or \"only\"");
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -1933,17 +1953,8 @@ static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc,
 {
        struct mgmt_cp_set_privacy cp;
 
-       if (argc < 2) {
-               print("Specify \"on\" or \"off\"");
+       if (parse_setting(argc, argv, &cp.privacy) == false)
                return noninteractive_quit(EXIT_FAILURE);
-       }
-
-       if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
-               cp.privacy = 0x01;
-       else if (strcasecmp(argv[1], "off") == 0)
-               cp.privacy = 0x00;
-       else
-               cp.privacy = atoi(argv[1]);
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -2006,7 +2017,7 @@ static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        uint8_t class[2];
 
        if (argc < 3) {
-               print("Usage: %s <major> <minor>", argv[0]);
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2051,11 +2062,6 @@ static void disconnect_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void disconnect_usage(void)
-{
-       print("Usage: disconnect [-t type] <remote address>");
-}
-
 static struct option disconnect_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -2068,6 +2074,7 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_cp_disconnect cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options,
                                                                NULL)) != -1) {
@@ -2076,11 +2083,11 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       disconnect_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       disconnect_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2091,7 +2098,7 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
        optind = 0;
 
        if (argc < 1) {
-               disconnect_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2163,11 +2170,6 @@ static void find_service_rsp(uint8_t status, uint16_t len, const void *param,
        discovery = true;
 }
 
-static void find_service_usage(void)
-{
-       print("Usage: find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]");
-}
-
 static struct option find_service_options[] = {
        { "help",       no_argument, 0, 'h' },
        { "le-only",    no_argument, 0, 'l' },
@@ -2201,6 +2203,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
        int8_t rssi;
        uint16_t count;
        int opt;
+       char *cmd = argv[0];
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -2209,11 +2212,11 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
        count = 0;
 
        if (argc == 1) {
-               find_service_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
-       while ((opt = getopt_long(argc, argv, "+lbu:r:p:h",
+       while ((opt = getopt_long(argc, argv, "+lbu:r:h",
                                        find_service_options, NULL)) != -1) {
                switch (opt) {
                case 'l':
@@ -2246,11 +2249,11 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
                        rssi = atoi(optarg);
                        break;
                case 'h':
-                       find_service_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       find_service_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2261,7 +2264,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
        optind = 0;
 
        if (argc > 0) {
-               find_service_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2291,11 +2294,6 @@ static void find_rsp(uint8_t status, uint16_t len, const void *param,
        discovery = true;
 }
 
-static void find_usage(void)
-{
-       print("Usage: find [-l|-b]>");
-}
-
 static struct option find_options[] = {
        { "help",       0, 0, 'h' },
        { "le-only",    1, 0, 'l' },
@@ -2310,6 +2308,7 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        uint8_t op = MGMT_OP_START_DISCOVERY;
        uint8_t type = SCAN_TYPE_DUAL;
        int opt;
+       char *cmd = argv[0];
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -2329,11 +2328,11 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
                        op = MGMT_OP_START_LIMITED_DISCOVERY;
                        break;
                case 'h':
-                       find_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       find_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2357,23 +2356,17 @@ static void stop_find_rsp(uint8_t status, uint16_t len, const void *param,
                                                        void *user_data)
 {
        if (status != 0) {
-               fprintf(stderr,
-                       "Stop Discovery failed: status 0x%02x (%s)\n",
+               error("Stop Discovery failed: status 0x%02x (%s)",
                                                status, mgmt_errstr(status));
                return noninteractive_quit(EXIT_SUCCESS);
        }
 
-       printf("Discovery stopped\n");
+       print("Discovery stopped");
        discovery = false;
 
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void stop_find_usage(void)
-{
-       printf("Usage: btmgmt stop-find [-l|-b]>\n");
-}
-
 static struct option stop_find_options[] = {
        { "help",       0, 0, 'h' },
        { "le-only",    1, 0, 'l' },
@@ -2387,6 +2380,7 @@ static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_cp_stop_discovery cp;
        uint8_t type = SCAN_TYPE_DUAL;
        int opt;
+       char *cmd = argv[0];
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -2404,9 +2398,9 @@ static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
                        break;
                case 'h':
                default:
-                       stop_find_usage();
+                       cmd_usage(cmd);
                        optind = 0;
-                       exit(EXIT_SUCCESS);
+                       return noninteractive_quit(EXIT_SUCCESS);
                }
        }
 
@@ -2419,8 +2413,8 @@ static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
 
        if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp,
                                             stop_find_rsp, NULL, NULL) == 0) {
-               fprintf(stderr, "Unable to send stop_discovery cmd\n");
-               exit(EXIT_FAILURE);
+               error("Unable to send stop_discovery cmd");
+               return noninteractive_quit(EXIT_FAILURE);
        }
 }
 
@@ -2439,7 +2433,7 @@ static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        struct mgmt_cp_set_local_name cp;
 
        if (argc < 2) {
-               print("Usage: %s <name> [shortname]", argv[0]);
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2491,11 +2485,6 @@ static void pair_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void pair_usage(void)
-{
-       print("Usage: pair [-c cap] [-t type] <remote address>");
-}
-
 static struct option pair_options[] = {
        { "help",       0, 0, 'h' },
        { "capability", 1, 0, 'c' },
@@ -2510,6 +2499,7 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        uint8_t type = BDADDR_BREDR;
        char addr[18];
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
                                                                NULL)) != -1) {
@@ -2521,11 +2511,11 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       pair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       pair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2536,7 +2526,7 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        optind = 0;
 
        if (argc < 1) {
-               pair_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2587,11 +2577,6 @@ static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void cancel_pair_usage(void)
-{
-       print("Usage: cancelpair [-t type] <remote address>");
-}
-
 static struct option cancel_pair_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -2604,6 +2589,7 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_addr_info cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", cancel_pair_options,
                                                                NULL)) != -1) {
@@ -2612,11 +2598,11 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       cancel_pair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       cancel_pair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2627,7 +2613,7 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
        optind = 0;
 
        if (argc < 1) {
-               cancel_pair_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2673,11 +2659,6 @@ static void unpair_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void unpair_usage(void)
-{
-       print("Usage: unpair [-t type] <remote address>");
-}
-
 static struct option unpair_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -2690,6 +2671,7 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_cp_unpair_device cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", unpair_options,
                                                                NULL)) != -1) {
@@ -2698,11 +2680,11 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       unpair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       unpair_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2713,7 +2695,7 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
        optind = 0;
 
        if (argc < 1) {
-               unpair_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2800,11 +2782,6 @@ static void irks_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void irks_usage(void)
-{
-       print("Usage: irks [--local]");
-}
-
 static struct option irks_options[] = {
        { "help",       0, 0, 'h' },
        { "local",      1, 0, 'l' },
@@ -2821,6 +2798,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        uint16_t count, local_index;
        char path[PATH_MAX];
        int opt;
+       char *cmd = argv[0];
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -2866,11 +2844,11 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
                        count++;
                        break;
                case 'h':
-                       irks_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       irks_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2881,7 +2859,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        optind = 0;
 
        if (argc > 0) {
-               irks_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2924,11 +2902,6 @@ static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void block_usage(void)
-{
-       print("Usage: block [-t type] <remote address>");
-}
-
 static struct option block_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -2940,6 +2913,7 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        struct mgmt_cp_block_device cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", block_options,
                                                        NULL)) != -1) {
@@ -2948,11 +2922,11 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       block_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       block_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -2963,7 +2937,7 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        optind = 0;
 
        if (argc < 1) {
-               block_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -2981,17 +2955,13 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        }
 }
 
-static void unblock_usage(void)
-{
-       print("Usage: unblock [-t type] <remote address>");
-}
-
 static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
                                                                char **argv)
 {
        struct mgmt_cp_unblock_device cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", block_options,
                                                        NULL)) != -1) {
@@ -3000,11 +2970,11 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       unblock_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       unblock_usage();
+                       cmd_usage(cmd);
                        optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
@@ -3015,7 +2985,7 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
        optind = 0;
 
        if (argc < 1) {
-               unblock_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3179,15 +3149,8 @@ static void remote_oob_rsp(uint8_t status, uint16_t len, const void *param,
        print("Remote OOB data added for %s (%u)", addr, rp->type);
 }
 
-static void remote_oob_usage(void)
-{
-       print("Usage: remote-oob [-t <addr_type>] "
-               "[-r <rand192>] [-h <hash192>] [-R <rand256>] [-H <hash256>] "
-               "<addr>");
-}
-
 static struct option remote_oob_opt[] = {
-       { "help",       0, 0, 'h' },
+       { "help",       0, 0, '?' },
        { "type",       1, 0, 't' },
        { 0, 0, 0, 0 }
 };
@@ -3197,6 +3160,7 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index,
 {
        struct mgmt_cp_add_remote_oob_data cp;
        int opt;
+       char *cmd = argv[0];
 
        memset(&cp, 0, sizeof(cp));
        cp.addr.type = BDADDR_BREDR;
@@ -3220,7 +3184,8 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index,
                        hex2bin(optarg, cp.hash256, 16);
                        break;
                default:
-                       remote_oob_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
        }
@@ -3230,7 +3195,7 @@ static void cmd_remote_oob(struct mgmt *mgmt, uint16_t index,
        optind = 0;
 
        if (argc < 1) {
-               remote_oob_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3263,7 +3228,7 @@ static void did_rsp(uint8_t status, uint16_t len, const void *param,
 
 static void did_usage(void)
 {
-       print("Usage: did <source>:<vendor>:<product>:<version>");
+       cmd_usage("did");
        print("       possible source values: bluetooth, usb");
 }
 
@@ -3323,18 +3288,13 @@ static void static_addr_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void static_addr_usage(void)
-{
-       print("Usage: static-addr <address>");
-}
-
 static void cmd_static_addr(struct mgmt *mgmt, uint16_t index,
                                                        int argc, char **argv)
 {
        struct mgmt_cp_set_static_address cp;
 
        if (argc < 2) {
-               static_addr_usage();
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3379,7 +3339,7 @@ static void cmd_public_addr(struct mgmt *mgmt, uint16_t index,
        struct mgmt_cp_set_public_address cp;
 
        if (argc < 2) {
-               print("Usage: public-addr <address>");
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3400,17 +3360,8 @@ static void cmd_ext_config(struct mgmt *mgmt, uint16_t index,
 {
        struct mgmt_cp_set_external_config cp;
 
-       if (argc < 2) {
-               print("Specify \"on\" or \"off\"");
+       if (parse_setting(argc, argv, &cp.config) == false)
                return noninteractive_quit(EXIT_FAILURE);
-       }
-
-       if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
-               cp.config = 0x01;
-       else if (strcasecmp(argv[1], "off") == 0)
-               cp.config = 0x00;
-       else
-               cp.config = atoi(argv[1]);
 
        if (index == MGMT_INDEX_NONE)
                index = 0;
@@ -3460,11 +3411,6 @@ static void conn_info_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void conn_info_usage(void)
-{
-       print("Usage: conn-info [-t type] <remote address>");
-}
-
 static struct option conn_info_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -3477,6 +3423,7 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index,
        struct mgmt_cp_get_conn_info cp;
        uint8_t type = BDADDR_BREDR;
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options,
                                                                NULL)) != -1) {
@@ -3485,10 +3432,12 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       conn_info_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       conn_info_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
        }
@@ -3498,7 +3447,7 @@ static void cmd_conn_info(struct mgmt *mgmt, uint16_t index,
        optind = 0;
 
        if (argc < 1) {
-               conn_info_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3528,11 +3477,6 @@ static void io_cap_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void io_cap_usage(void)
-{
-       print("Usage: io-cap <cap>");
-}
-
 static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
                                                int argc, char **argv)
 {
@@ -3540,7 +3484,7 @@ static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
        uint8_t cap;
 
        if (argc < 2) {
-               io_cap_usage();
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3570,18 +3514,13 @@ static void scan_params_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void scan_params_usage(void)
-{
-       print("Usage: scan-params <interval> <window>");
-}
-
 static void cmd_scan_params(struct mgmt *mgmt, uint16_t index,
                                                        int argc, char **argv)
 {
        struct mgmt_cp_set_scan_params cp;
 
        if (argc < 3) {
-               scan_params_usage();
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3650,11 +3589,6 @@ static void add_device_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void add_device_usage(void)
-{
-       print("Usage: add-device [-a action] [-t type] <address>");
-}
-
 static struct option add_device_options[] = {
        { "help",       0, 0, 'h' },
        { "action",     1, 0, 'a' },
@@ -3670,6 +3604,7 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index,
        uint8_t type = BDADDR_BREDR;
        char addr[18];
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+a:t:h", add_device_options,
                                                                NULL)) != -1) {
@@ -3681,10 +3616,12 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       add_device_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       add_device_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
        }
@@ -3694,7 +3631,7 @@ static void cmd_add_device(struct mgmt *mgmt, uint16_t index,
        optind = 0;
 
        if (argc < 1) {
-               add_device_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3725,11 +3662,6 @@ static void remove_device_rsp(uint8_t status, uint16_t len, const void *param,
        noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void del_device_usage(void)
-{
-       print("Usage: del-device [-t type] <address>");
-}
-
 static struct option del_device_options[] = {
        { "help",       0, 0, 'h' },
        { "type",       1, 0, 't' },
@@ -3743,6 +3675,7 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index,
        uint8_t type = BDADDR_BREDR;
        char addr[18];
        int opt;
+       char *cmd = argv[0];
 
        while ((opt = getopt_long(argc, argv, "+t:h", del_device_options,
                                                                NULL)) != -1) {
@@ -3751,10 +3684,12 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index,
                        type = strtol(optarg, NULL, 0);
                        break;
                case 'h':
-                       del_device_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_SUCCESS);
                default:
-                       del_device_usage();
+                       cmd_usage(cmd);
+                       optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
        }
@@ -3764,7 +3699,7 @@ static void cmd_del_device(struct mgmt *mgmt, uint16_t index,
        optind = 0;
 
        if (argc < 1) {
-               del_device_usage();
+               cmd_usage(cmd);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -3963,7 +3898,8 @@ static void adv_size_info_rsp(uint8_t status, uint16_t len, const void *param,
 
 static void advsize_usage(void)
 {
-       print("Usage: advsize [options] <instance_id>\nOptions:\n"
+       cmd_usage("advsize");
+       print("Options:\n"
                "\t -c, --connectable         \"connectable\" flag\n"
                "\t -g, --general-discov      \"general-discoverable\" flag\n"
                "\t -l, --limited-discov      \"limited-discoverable\" flag\n"
@@ -4019,6 +3955,7 @@ static void cmd_advsize(struct mgmt *mgmt, uint16_t index,
                        break;
                default:
                        advsize_usage();
+                       optind = 0;
                        return noninteractive_quit(EXIT_FAILURE);
                }
        }
@@ -4072,7 +4009,8 @@ static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
 
 static void add_adv_usage(void)
 {
-       print("Usage: add-adv [options] <instance_id>\nOptions:\n"
+       cmd_usage("add-adv");
+       print("Options:\n"
                "\t -u, --uuid <uuid>         Service UUID\n"
                "\t -d, --adv-data <data>     Advertising Data bytes\n"
                "\t -s, --scan-rsp <data>     Scan Response Data bytes\n"
@@ -4251,8 +4189,10 @@ static void cmd_add_adv(struct mgmt *mgmt, uint16_t index,
                        break;
                case 'h':
                        success = true;
+                       /* fall through */
                default:
                        add_adv_usage();
+                       optind = 0;
                        goto done;
                }
        }
@@ -4333,18 +4273,13 @@ static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param,
        return noninteractive_quit(EXIT_SUCCESS);
 }
 
-static void rm_adv_usage(void)
-{
-       print("Usage: rm-adv <instance_id>");
-}
-
 static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
 {
        struct mgmt_cp_remove_advertising cp;
        uint8_t instance;
 
        if (argc != 2) {
-               rm_adv_usage();
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -4390,7 +4325,7 @@ static void cmd_appearance(struct mgmt *mgmt, uint16_t index, int argc,
        struct mgmt_cp_set_appearance cp;
 
        if (argc < 2) {
-               print("Usage: appearance <appearance>");
+               cmd_usage(argv[0]);
                return noninteractive_quit(EXIT_FAILURE);
        }
 
@@ -4408,6 +4343,7 @@ static void cmd_appearance(struct mgmt *mgmt, uint16_t index, int argc,
 
 struct cmd_info {
        char *cmd;
+       const char *arg;
        void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
        char *doc;
        char * (*gen) (const char *text, int state);
@@ -4415,66 +4351,128 @@ struct cmd_info {
 };
 
 static struct cmd_info all_cmd[] = {
-       { "version",    cmd_version,    "Get the MGMT Version"          },
-       { "commands",   cmd_commands,   "List supported commands"       },
-       { "config",     cmd_config,     "Show configuration info"       },
-       { "info",       cmd_info,       "Show controller info"          },
-       { "extinfo",    cmd_extinfo,    "Show extended controller info" },
-       { "auto-power", cmd_auto_power, "Power all available features"  },
-       { "power",      cmd_power,      "Toggle powered state"          },
-       { "discov",     cmd_discov,     "Toggle discoverable state"     },
-       { "connectable",cmd_connectable,"Toggle connectable state"      },
-       { "fast-conn",  cmd_fast_conn,  "Toggle fast connectable state" },
-       { "bondable",   cmd_bondable,   "Toggle bondable state"         },
-       { "pairable",   cmd_bondable,   "Toggle bondable state"         },
-       { "linksec",    cmd_linksec,    "Toggle link level security"    },
-       { "ssp",        cmd_ssp,        "Toggle SSP mode"               },
-       { "sc",         cmd_sc,         "Toogle SC support"             },
-       { "hs",         cmd_hs,         "Toggle HS support"             },
-       { "le",         cmd_le,         "Toggle LE support"             },
-       { "advertising",cmd_advertising,"Toggle LE advertising",        },
-       { "bredr",      cmd_bredr,      "Toggle BR/EDR support",        },
-       { "privacy",    cmd_privacy,    "Toggle privacy support"        },
-       { "class",      cmd_class,      "Set device major/minor class"  },
-       { "disconnect", cmd_disconnect, "Disconnect device"             },
-       { "con",        cmd_con,        "List connections"              },
-       { "find",       cmd_find,       "Discover nearby devices"       },
-       { "find-service", cmd_find_service, "Discover nearby service"   },
-       { "stop-find",  cmd_stop_find,  "Stop discovery"                },
-       { "name",       cmd_name,       "Set local name"                },
-       { "pair",       cmd_pair,       "Pair with a remote device"     },
-       { "cancelpair", cmd_cancel_pair,"Cancel pairing"                },
-       { "unpair",     cmd_unpair,     "Unpair device"                 },
-       { "keys",       cmd_keys,       "Load Link Keys"                },
-       { "ltks",       cmd_ltks,       "Load Long Term Keys"           },
-       { "irks",       cmd_irks,       "Load Identity Resolving Keys"  },
-       { "block",      cmd_block,      "Block Device"                  },
-       { "unblock",    cmd_unblock,    "Unblock Device"                },
-       { "add-uuid",   cmd_add_uuid,   "Add UUID"                      },
-       { "rm-uuid",    cmd_remove_uuid,"Remove UUID"                   },
-       { "clr-uuids",  cmd_clr_uuids,  "Clear UUIDs"                   },
-       { "local-oob",  cmd_local_oob,  "Local OOB data"                },
-       { "remote-oob", cmd_remote_oob, "Remote OOB data"               },
-       { "did",        cmd_did,        "Set Device ID"                 },
-       { "static-addr",cmd_static_addr,"Set static address"            },
-       { "public-addr",cmd_public_addr,"Set public address"            },
-       { "ext-config", cmd_ext_config, "External configuration"        },
-       { "debug-keys", cmd_debug_keys, "Toogle debug keys"             },
-       { "conn-info",  cmd_conn_info,  "Get connection information"    },
-       { "io-cap",     cmd_io_cap,     "Set IO Capability"             },
-       { "scan-params",cmd_scan_params,"Set Scan Parameters"           },
-       { "get-clock",  cmd_clock_info, "Get Clock Information"         },
-       { "add-device", cmd_add_device, "Add Device"                    },
-       { "del-device", cmd_del_device, "Remove Device"                 },
-       { "clr-devices",cmd_clr_devices,"Clear Devices"                 },
-       { "bredr-oob",  cmd_bredr_oob,  "Local OOB data (BR/EDR)"       },
-       { "le-oob",     cmd_le_oob,     "Local OOB data (LE)"           },
-       { "advinfo",    cmd_advinfo,    "Show advertising features"     },
-       { "advsize",    cmd_advsize,    "Show advertising size info"    },
-       { "add-adv",    cmd_add_adv,    "Add advertising instance"      },
-       { "rm-adv",     cmd_rm_adv,     "Remove advertising instance"   },
-       { "clr-adv",    cmd_clr_adv,    "Clear advertising instances"   },
-       { "appearance", cmd_appearance, "Set appearance"                },
+       { "version",            NULL,
+               cmd_version,            "Get the MGMT Version"          },
+       { "commands",           NULL,
+               cmd_commands,           "List supported commands"       },
+       { "config",             NULL,
+               cmd_config,             "Show configuration info"       },
+       { "info",               NULL,
+               cmd_info,               "Show controller info"          },
+       { "extinfo",            NULL,
+               cmd_extinfo,            "Show extended controller info" },
+       { "auto-power",         NULL,
+               cmd_auto_power,         "Power all available features"  },
+       { "power",              "<on/off>",
+               cmd_power,              "Toggle powered state"          },
+       { "discov",             "<yes/no/limited> [timeout]",
+               cmd_discov,             "Toggle discoverable state"     },
+       { "connectable",        "<on/off>",
+       cmd_connectable,                "Toggle connectable state"      },
+       { "fast-conn",          "<on/off>",
+               cmd_fast_conn,          "Toggle fast connectable state" },
+       { "bondable",           "<on/off>",
+               cmd_bondable,           "Toggle bondable state"         },
+       { "pairable",           "<on/off>",
+               cmd_bondable,           "Toggle bondable state"         },
+       { "linksec",            "<on/off>",
+               cmd_linksec,            "Toggle link level security"    },
+       { "ssp",                "<on/off>",
+               cmd_ssp,                "Toggle SSP mode"               },
+       { "sc",                 "<on/off/only>",
+               cmd_sc,                 "Toogle SC support"             },
+       { "hs",                 "<on/off>",
+               cmd_hs,                 "Toggle HS support"             },
+       { "le",                 "<on/off>",
+               cmd_le,                 "Toggle LE support"             },
+       { "advertising",        "<on/off>",
+       cmd_advertising,                "Toggle LE advertising",        },
+       { "bredr",              "<on/off>",
+               cmd_bredr,              "Toggle BR/EDR support",        },
+       { "privacy",            "<on/off>",
+               cmd_privacy,            "Toggle privacy support"        },
+       { "class",              "<major> <minor>",
+               cmd_class,              "Set device major/minor class"  },
+       { "disconnect",         "[-t type] <remote address>",
+               cmd_disconnect,         "Disconnect device"             },
+       { "con",                NULL,
+               cmd_con,                "List connections"              },
+       { "find",               "[-l|-b] [-L]",
+               cmd_find,               "Discover nearby devices"       },
+       { "find-service",       "[-u UUID] [-r RSSI_Threshold] [-l|-b]",
+               cmd_find_service,       "Discover nearby service"       },
+       { "stop-find",          "[-l|-b]",
+               cmd_stop_find,          "Stop discovery"                },
+       { "name",               "<name> [shortname]",
+               cmd_name,               "Set local name"                },
+       { "pair",               "[-c cap] [-t type] <remote address>",
+               cmd_pair,               "Pair with a remote device"     },
+       { "cancelpair",         "[-t type] <remote address>",
+               cmd_cancel_pair,        "Cancel pairing"                },
+       { "unpair",             "[-t type] <remote address>",
+               cmd_unpair,             "Unpair device"                 },
+       { "keys",               NULL,
+               cmd_keys,               "Load Link Keys"                },
+       { "ltks",               NULL,
+               cmd_ltks,               "Load Long Term Keys"           },
+       { "irks",               "[--local <index>] [--file <file path>]",
+               cmd_irks,               "Load Identity Resolving Keys"  },
+       { "block",              "[-t type] <remote address>",
+               cmd_block,              "Block Device"                  },
+       { "unblock",            "[-t type] <remote address>",
+               cmd_unblock,            "Unblock Device"                },
+       { "add-uuid",           "<UUID> <service class hint>",
+               cmd_add_uuid,           "Add UUID"                      },
+       { "rm-uuid",            "<UUID>",
+               cmd_remove_uuid,        "Remove UUID"                   },
+       { "clr-uuids",          NULL,
+               cmd_clr_uuids,          "Clear UUIDs"                   },
+       { "local-oob",          NULL,
+               cmd_local_oob,          "Local OOB data"                },
+       { "remote-oob",         "[-t <addr_type>] [-r <rand192>] "
+                               "[-h <hash192>] [-R <rand256>] "
+                               "[-H <hash256>] <addr>",
+               cmd_remote_oob,         "Remote OOB data"               },
+       { "did",                "<source>:<vendor>:<product>:<version>",
+               cmd_did,                "Set Device ID"                 },
+       { "static-addr",        "<address>",
+               cmd_static_addr,        "Set static address"            },
+       { "public-addr",        "<address>",
+               cmd_public_addr,        "Set public address"            },
+       { "ext-config",         "<on/off>",
+               cmd_ext_config,         "External configuration"        },
+       { "debug-keys",         "<on/off>",
+               cmd_debug_keys,         "Toogle debug keys"             },
+       { "conn-info",          "[-t type] <remote address>",
+               cmd_conn_info,          "Get connection information"    },
+       { "io-cap",             "<cap>",
+               cmd_io_cap,             "Set IO Capability"             },
+       { "scan-params",        "<interval> <window>",
+               cmd_scan_params,        "Set Scan Parameters"           },
+       { "get-clock",          "[address]",
+               cmd_clock_info,         "Get Clock Information"         },
+       { "add-device",         "[-a action] [-t type] <address>",
+               cmd_add_device,         "Add Device"                    },
+       { "del-device",         "[-t type] <address>",
+               cmd_del_device,         "Remove Device"                 },
+       { "clr-devices",        NULL,
+               cmd_clr_devices,        "Clear Devices"                 },
+       { "bredr-oob",          NULL,
+               cmd_bredr_oob,          "Local OOB data (BR/EDR)"       },
+       { "le-oob",             NULL,
+               cmd_le_oob,             "Local OOB data (LE)"           },
+       { "advinfo",            NULL,
+               cmd_advinfo,            "Show advertising features"     },
+       { "advsize",            "[options] <instance_id>",
+               cmd_advsize,            "Show advertising size info"    },
+       { "add-adv",            "[options] <instance_id>",
+               cmd_add_adv,            "Add advertising instance"      },
+       { "rm-adv",             "<instance_id>",
+               cmd_rm_adv,             "Remove advertising instance"   },
+       { "clr-adv",            NULL,
+               cmd_clr_adv,            "Clear advertising instances"   },
+       { "appearance",         "<appearance>",
+               cmd_appearance,         "Set appearance"                },
 };
 
 static void cmd_quit(struct mgmt *mgmt, uint16_t index,
@@ -4541,20 +4539,14 @@ static void cmd_select(struct mgmt *mgmt, uint16_t index,
                                                int argc, char **argv)
 {
        if (argc != 2) {
-               error("Usage: select <index>");
+               cmd_usage(argv[0]);
                return;
        }
 
        mgmt_cancel_all(mgmt);
        mgmt_unregister_all(mgmt);
 
-       if (!strcmp(argv[1], "none") || !strcmp(argv[1], "any") ||
-                                               !strcmp(argv[1], "all"))
-               mgmt_index = MGMT_INDEX_NONE;
-       else if (!strncmp(argv[1], "hci", 3))
-               mgmt_index = atoi(&argv[1][3]);
-       else
-               mgmt_index = atoi(argv[1]);
+       set_index(argv[1]);
 
        register_mgmt_callbacks(mgmt, mgmt_index);
 
@@ -4564,10 +4556,14 @@ static void cmd_select(struct mgmt *mgmt, uint16_t index,
 }
 
 static struct cmd_info interactive_cmd[] = {
-       { "select",     cmd_select,     "Select a different index"      },
-       { "quit",       cmd_quit,       "Exit program"                  },
-       { "exit",       cmd_quit,       "Exit program"                  },
-       { "help",       NULL,           "List supported commands"       },
+       { "select",     "<index>",
+                       cmd_select,     "Select a different index"      },
+       { "quit",       NULL,
+                       cmd_quit,       "Exit program"                  },
+       { "exit",       NULL,
+                       cmd_quit,       "Exit program"                  },
+       { "help",       NULL,
+                       NULL,           "List supported commands"       },
 };
 
 static char *cmd_generator(const char *text, int state)
@@ -4642,6 +4638,29 @@ static struct cmd_info *find_cmd(const char *cmd, struct cmd_info table[],
        return NULL;
 }
 
+static void cmd_usage(char *cmd)
+{
+       struct cmd_info *c;
+
+       if (!cmd)
+               return;
+
+       c = find_cmd(cmd, all_cmd, NELEM(all_cmd));
+       if (!c && interactive) {
+               c = find_cmd(cmd, interactive_cmd, NELEM(interactive_cmd));
+               if (!c)
+                       return;
+               error("Usage: %s %s", cmd, c->arg ? : "");
+               return;
+       }
+
+       if (!c)
+               return;
+
+       print("Usage: %s %s", cmd, c->arg ? : "");
+
+}
+
 static void rl_handler(char *input)
 {
        struct cmd_info *c;
@@ -4663,7 +4682,8 @@ static void rl_handler(char *input)
        if (prompt_input(input))
                goto done;
 
-       add_history(input);
+       if (history_search(input, -1))
+               add_history(input);
 
        if (wordexp(input, &w, WRDE_NOCMD))
                goto done;
@@ -4693,9 +4713,18 @@ static void rl_handler(char *input)
 
        for (i = 0; i < NELEM(all_cmd); i++) {
                c = &all_cmd[i];
-               if (c->doc)
-                       print("  %s %-*s %s", c->cmd,
-                               (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+               if ((int)strlen(c->arg ? : "") <=
+                                       (int)(25 - strlen(c->cmd)))
+                       printf("  %s %-*s %s\n", c->cmd,
+                                       (int)(25 - strlen(c->cmd)),
+                                       c->arg ? : "",
+                                       c->doc ? : "");
+               else
+                       printf("  %s %-s\n" "  %s %-25s %s\n",
+                                       c->cmd,
+                                       c->arg ? : "",
+                                       "", "",
+                                       c->doc ? : "");
        }
 
        if (!interactive)
@@ -4703,9 +4732,18 @@ static void rl_handler(char *input)
 
        for (i = 0; i < NELEM(interactive_cmd); i++) {
                c = &interactive_cmd[i];
-               if (c->doc)
-                       print("  %s %-*s %s", c->cmd,
-                               (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+               if ((int)strlen(c->arg ? : "") <=
+                                       (int)(25 - strlen(c->cmd)))
+                       printf("  %s %-*s %s\n", c->cmd,
+                                       (int)(25 - strlen(c->cmd)),
+                                       c->arg ? : "",
+                                       c->doc ? : "");
+               else
+                       printf("  %s %-s\n" "  %s %-25s %s\n",
+                                       c->cmd,
+                                       c->arg ? : "",
+                                       "", "",
+                                       c->doc ? : "");
        }
 
 free_we:
@@ -4772,18 +4810,13 @@ static void mgmt_debug(const char *str, void *user_data)
 int main(int argc, char *argv[])
 {
        struct io *input;
-       uint16_t index = MGMT_INDEX_NONE;
        int status, opt;
 
        while ((opt = getopt_long(argc, argv, "+hi:",
                                                main_options, NULL)) != -1) {
                switch (opt) {
                case 'i':
-                       if (strlen(optarg) > 3 &&
-                                       strncasecmp(optarg, "hci", 3) == 0)
-                               index = atoi(optarg + 3);
-                       else
-                               index = atoi(optarg);
+                       set_index(optarg);
                        break;
                case 'h':
                default:
@@ -4817,10 +4850,10 @@ int main(int argc, char *argv[])
                        return EXIT_FAILURE;
                }
 
-               c->func(mgmt, index, argc, argv);
+               c->func(mgmt, mgmt_index, argc, argv);
        }
 
-       register_mgmt_callbacks(mgmt, index);
+       register_mgmt_callbacks(mgmt, mgmt_index);
 
        /* Interactive mode */
        if (!argc)
@@ -4836,12 +4869,10 @@ int main(int argc, char *argv[])
                rl_erase_empty_line = 1;
                rl_callback_handler_install(NULL, rl_handler);
 
-               update_prompt(index);
+               update_prompt(mgmt_index);
                rl_redisplay();
        }
 
-       mgmt_index = index;
-
        status = mainloop_run();
 
        if (input) {
diff --git a/tools/btpclient.c b/tools/btpclient.c
new file mode 100644 (file)
index 0000000..9ea6249
--- /dev/null
@@ -0,0 +1,893 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2017  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
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <getopt.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/btp.h"
+
+struct btp_adapter {
+       struct l_dbus_proxy *proxy;
+       uint8_t index;
+       uint32_t supported_settings;
+       uint32_t current_settings;
+       struct l_queue *devices;
+};
+
+struct btp_device {
+       struct l_dbus_proxy *proxy;
+};
+
+static struct l_queue *adapters;
+static char *socket_path;
+static struct btp *btp;
+
+static bool gap_service_registered;
+
+static struct btp_adapter *find_adapter_by_proxy(struct l_dbus_proxy *proxy)
+{
+       const struct l_queue_entry *entry;
+
+       for (entry = l_queue_get_entries(adapters); entry;
+                                                       entry = entry->next) {
+               struct btp_adapter *adapter = entry->data;
+
+               if (adapter->proxy == proxy)
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static struct btp_adapter *find_adapter_by_index(uint8_t index)
+{
+       const struct l_queue_entry *entry;
+
+       for (entry = l_queue_get_entries(adapters); entry;
+                                                       entry = entry->next) {
+               struct btp_adapter *adapter = entry->data;
+
+               if (adapter->index == index)
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static struct btp_adapter *find_adapter_by_path(const char *path)
+{
+       const struct l_queue_entry *entry;
+
+       for (entry = l_queue_get_entries(adapters); entry;
+                                                       entry = entry->next) {
+               struct btp_adapter *adapter = entry->data;
+
+               if (!strcmp(l_dbus_proxy_get_path(adapter->proxy), path))
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static void btp_gap_read_commands(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       uint16_t commands = 0;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_GAP_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       commands |= (1 << BTP_OP_GAP_READ_SUPPORTED_COMMANDS);
+       commands |= (1 << BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST);
+       commands |= (1 << BTP_OP_GAP_READ_COTROLLER_INFO);
+       commands |= (1 << BTP_OP_GAP_RESET);
+       commands |= (1 << BTP_OP_GAP_SET_POWERED);
+       commands |= (1 << BTP_OP_GAP_SET_DISCOVERABLE);
+       commands |= (1 << BTP_OP_GAP_SET_BONDABLE);
+
+       commands = L_CPU_TO_LE16(commands);
+
+       btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_SUPPORTED_COMMANDS,
+                       BTP_INDEX_NON_CONTROLLER, sizeof(commands), &commands);
+}
+
+static void btp_gap_read_controller_index(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       const struct l_queue_entry *entry;
+       struct btp_gap_read_index_rp *rp;
+       uint8_t cnt;
+       int i;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_GAP_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       cnt = l_queue_length(adapters);
+
+       rp = l_malloc(sizeof(*rp) + cnt);
+
+       rp->num = cnt;
+
+       for (i = 0, entry = l_queue_get_entries(adapters); entry;
+                                               i++, entry = entry->next) {
+               struct btp_adapter *adapter = entry->data;
+
+               rp->indexes[i] = adapter->index;
+       }
+
+       btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST,
+                       BTP_INDEX_NON_CONTROLLER, sizeof(*rp) + cnt, rp);
+}
+
+static void btp_gap_read_info(uint8_t index, const void *param, uint16_t length,
+                                                               void *user_data)
+{
+       struct btp_adapter *adapter = find_adapter_by_index(index);
+       struct btp_gap_read_info_rp rp;
+       const char *str;
+       uint8_t status = BTP_ERROR_FAIL;
+
+       if (!adapter) {
+               status = BTP_ERROR_INVALID_INDEX;
+               goto failed;
+       }
+
+       memset(&rp, 0, sizeof(rp));
+
+       if (!l_dbus_proxy_get_property(adapter->proxy, "Address", "s", &str))
+               goto failed;
+
+       if (sscanf(str,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                       &rp.address[5], &rp.address[4], &rp.address[3],
+                       &rp.address[2], &rp.address[1], &rp.address[0]) != 6)
+               goto failed;
+
+       if (!l_dbus_proxy_get_property(adapter->proxy, "Name", "s", &str)) {
+               goto failed;
+       }
+
+       strncpy((char *) rp.name, str, sizeof(rp.name));
+       strncpy((char *) rp.short_name, str, sizeof(rp.short_name));
+       rp.supported_settings = L_CPU_TO_LE32(adapter->supported_settings);
+       rp.current_settings = L_CPU_TO_LE32(adapter->current_settings);
+
+       btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_COTROLLER_INFO, index,
+                                                       sizeof(rp), &rp);
+
+       return;
+failed:
+       btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void remove_device_setup(struct l_dbus_message *message,
+                                                       void *user_data)
+{
+       struct btp_device *device = user_data;
+
+       l_dbus_message_set_arguments(message, "o",
+                                       l_dbus_proxy_get_path(device->proxy));
+}
+
+static void remove_device_reply(struct l_dbus_proxy *proxy,
+                                               struct l_dbus_message *result,
+                                               void *user_data)
+{
+       struct btp_device *device = user_data;
+       struct btp_adapter *adapter = find_adapter_by_proxy(proxy);
+
+       if (!adapter)
+               return;
+
+       if (l_dbus_message_is_error(result)) {
+               const char *name;
+
+               l_dbus_message_get_error(result, &name, NULL);
+
+               l_error("Failed to remove device %s (%s)",
+                                       l_dbus_proxy_get_path(device->proxy),
+                                       name);
+               return;
+       }
+
+       l_queue_remove(adapter->devices, device);
+}
+
+static void btp_gap_reset(uint8_t index, const void *param, uint16_t length,
+                                                               void *user_data)
+{
+       struct btp_adapter *adapter = find_adapter_by_index(index);
+       const struct l_queue_entry *entry;
+       uint8_t status;
+       bool prop;
+
+       if (!adapter) {
+               status = BTP_ERROR_INVALID_INDEX;
+               goto failed;
+       }
+
+       /* Adapter needs to be powered to be able to remove devices */
+       if (!l_dbus_proxy_get_property(adapter->proxy, "Powered", "b", &prop) ||
+                                                                       !prop) {
+               status = BTP_ERROR_FAIL;
+               goto failed;
+       }
+
+       for (entry = l_queue_get_entries(adapter->devices); entry;
+                                                       entry = entry->next) {
+               struct btp_device *device = entry->data;
+
+               l_dbus_proxy_method_call(adapter->proxy, "RemoveDevice",
+                                               remove_device_setup,
+                                               remove_device_reply, device,
+                                               NULL);
+       }
+
+       /* TODO for we assume all went well */
+       btp_send(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET, index, 0, NULL);
+       return;
+
+failed:
+       btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+struct set_setting_data {
+       struct btp_adapter *adapter;
+       uint8_t opcode;
+       uint32_t setting;
+       bool value;
+};
+
+static void set_setting_reply(struct l_dbus_proxy *proxy,
+                               struct l_dbus_message *result, void *user_data)
+{
+       struct set_setting_data *data = user_data;
+       struct btp_adapter *adapter = data->adapter;
+       uint32_t settings;
+
+       if (l_dbus_message_is_error(result)) {
+               btp_send_error(btp, BTP_GAP_SERVICE, data->adapter->index,
+                                                               BTP_ERROR_FAIL);
+               return;
+       }
+
+       if (data->value)
+               adapter->current_settings |= data->setting;
+       else
+               adapter->current_settings &= ~data->setting;
+
+       settings = L_CPU_TO_LE32(adapter->current_settings);
+
+       btp_send(btp, BTP_GAP_SERVICE, data->opcode, adapter->index,
+                                               sizeof(settings), &settings);
+}
+
+static void btp_gap_set_powered(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       struct btp_adapter *adapter = find_adapter_by_index(index);
+       const struct btp_gap_set_powered_cp *cp = param;
+       uint8_t status = BTP_ERROR_FAIL;
+       struct set_setting_data *data;
+
+       if (length < sizeof(*cp))
+               goto failed;
+
+       if (!adapter) {
+               status = BTP_ERROR_INVALID_INDEX;
+               goto failed;
+       }
+
+       data = l_new(struct set_setting_data, 1);
+       data->adapter = adapter;
+       data->opcode = BTP_OP_GAP_SET_POWERED;
+       data->setting = BTP_GAP_SETTING_POWERED;
+       data->value = cp->powered;
+
+       if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+                                       data, l_free, "Powered", "b",
+                                       data->value))
+               return;
+
+       l_free(data);
+
+failed:
+       btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void btp_gap_set_discoverable(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       struct btp_adapter *adapter = find_adapter_by_index(index);
+       const struct btp_gap_set_discoverable_cp *cp = param;
+       uint8_t status = BTP_ERROR_FAIL;
+       struct set_setting_data *data;
+
+       if (length < sizeof(*cp))
+               goto failed;
+
+       if (!adapter) {
+               status = BTP_ERROR_INVALID_INDEX;
+               goto failed;
+       }
+
+       data = l_new(struct set_setting_data, 1);
+       data->adapter = adapter;
+       data->opcode = BTP_OP_GAP_SET_DISCOVERABLE;
+       data->setting = BTP_GAP_SETTING_DISCOVERABLE;
+       data->value = cp->discoverable;
+
+       if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+                                       data, l_free, "Discoverable", "b",
+                                       data->value))
+               return;
+
+       l_free(data);
+
+failed:
+       btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void btp_gap_set_bondable(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       struct btp_adapter *adapter = find_adapter_by_index(index);
+       const struct btp_gap_set_bondable_cp *cp = param;
+       uint8_t status = BTP_ERROR_FAIL;
+       struct set_setting_data *data;
+
+       if (length < sizeof(*cp))
+               goto failed;
+
+       if (!adapter) {
+               status = BTP_ERROR_INVALID_INDEX;
+               goto failed;
+       }
+
+       data = l_new(struct set_setting_data, 1);
+       data->adapter = adapter;
+       data->opcode = BTP_OP_GAP_SET_BONDABLE;
+       data->setting = BTP_GAP_SETTING_BONDABLE;
+       data->value = cp->bondable;
+
+       if (l_dbus_proxy_set_property(adapter->proxy, set_setting_reply,
+                                       data, l_free, "Pairable", "b",
+                                       data->value))
+               return;
+
+       l_free(data);
+
+failed:
+       btp_send_error(btp, BTP_GAP_SERVICE, index, status);
+}
+
+static void register_gap_service(void)
+{
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_SUPPORTED_COMMANDS,
+                                       btp_gap_read_commands, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE,
+                               BTP_OP_GAP_READ_CONTROLLER_INDEX_LIST,
+                               btp_gap_read_controller_index, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_READ_COTROLLER_INFO,
+                                               btp_gap_read_info, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_RESET,
+                                               btp_gap_reset, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_POWERED,
+                                       btp_gap_set_powered, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_DISCOVERABLE,
+                                       btp_gap_set_discoverable, NULL, NULL);
+
+       btp_register(btp, BTP_GAP_SERVICE, BTP_OP_GAP_SET_BONDABLE,
+                                       btp_gap_set_bondable, NULL, NULL);
+}
+
+static void btp_core_read_commands(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       uint8_t commands = 0;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_CORE_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_COMMANDS);
+       commands |= (1 << BTP_OP_CORE_READ_SUPPORTED_SERVICES);
+       commands |= (1 << BTP_OP_CORE_REGISTER);
+       commands |= (1 << BTP_OP_CORE_UNREGISTER);
+
+       btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+                       BTP_INDEX_NON_CONTROLLER, sizeof(commands), &commands);
+}
+
+static void btp_core_read_services(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       uint8_t services = 0;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_CORE_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       services |= (1 << BTP_CORE_SERVICE);
+       services |= (1 << BTP_GAP_SERVICE);
+
+       btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+                       BTP_INDEX_NON_CONTROLLER, sizeof(services), &services);
+}
+
+static void btp_core_register(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       const struct btp_core_register_cp  *cp = param;
+
+       if (length < sizeof(*cp))
+               goto failed;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_CORE_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       switch (cp->service_id) {
+       case BTP_GAP_SERVICE:
+               if (gap_service_registered)
+                       goto failed;
+
+               register_gap_service();
+               gap_service_registered = true;
+               break;
+       case BTP_GATT_SERVICE:
+       case BTP_L2CAP_SERVICE:
+       case BTP_MESH_NODE_SERVICE:
+       case BTP_CORE_SERVICE:
+       default:
+               goto failed;
+       }
+
+       btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+                                       BTP_INDEX_NON_CONTROLLER, 0, NULL);
+       return;
+
+failed:
+       btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void btp_core_unregister(uint8_t index, const void *param,
+                                       uint16_t length, void *user_data)
+{
+       const struct btp_core_unregister_cp  *cp = param;
+
+       if (length < sizeof(*cp))
+               goto failed;
+
+       if (index != BTP_INDEX_NON_CONTROLLER) {
+               btp_send_error(btp, BTP_CORE_SERVICE, index,
+                                               BTP_ERROR_INVALID_INDEX);
+               return;
+       }
+
+       switch (cp->service_id) {
+       case BTP_GAP_SERVICE:
+               if (!gap_service_registered)
+                       goto failed;
+
+               btp_unregister_service(btp, BTP_GAP_SERVICE);
+               gap_service_registered = false;
+               break;
+       case BTP_GATT_SERVICE:
+       case BTP_L2CAP_SERVICE:
+       case BTP_MESH_NODE_SERVICE:
+       case BTP_CORE_SERVICE:
+       default:
+               goto failed;
+       }
+
+       btp_send(btp, BTP_CORE_SERVICE, BTP_OP_CORE_UNREGISTER,
+                                       BTP_INDEX_NON_CONTROLLER, 0, NULL);
+       return;
+
+failed:
+       btp_send_error(btp, BTP_CORE_SERVICE, index, BTP_ERROR_FAIL);
+}
+
+static void register_core_service(void)
+{
+       btp_register(btp, BTP_CORE_SERVICE,
+                                       BTP_OP_CORE_READ_SUPPORTED_COMMANDS,
+                                       btp_core_read_commands, NULL, NULL);
+
+       btp_register(btp, BTP_CORE_SERVICE,
+                                       BTP_OP_CORE_READ_SUPPORTED_SERVICES,
+                                       btp_core_read_services, NULL, NULL);
+
+       btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_REGISTER,
+                                               btp_core_register, NULL, NULL);
+
+       btp_register(btp, BTP_CORE_SERVICE, BTP_OP_CORE_UNREGISTER,
+                                       btp_core_unregister, NULL, NULL);
+}
+
+static void signal_handler(struct l_signal *signal, uint32_t signo,
+                                                       void *user_data)
+{
+       switch (signo) {
+       case SIGINT:
+       case SIGTERM:
+               l_info("Terminating");
+               l_main_quit();
+               break;
+       }
+}
+
+static void btp_device_free(struct btp_device *device)
+{
+       l_free(device);
+}
+
+static void btp_adapter_free(struct btp_adapter *adapter)
+{
+       l_queue_destroy(adapter->devices,
+                               (l_queue_destroy_func_t)btp_device_free);
+       l_free(adapter);
+}
+
+static void extract_settings(struct l_dbus_proxy *proxy, uint32_t *current,
+                                               uint32_t *supported)
+{
+       bool prop;
+
+       *supported = 0;
+       *current = 0;
+
+       /* TODO not all info is available via D-Bus API */
+       *supported |=  BTP_GAP_SETTING_POWERED;
+       *supported |=  BTP_GAP_SETTING_CONNECTABLE;
+       *supported |=  BTP_GAP_SETTING_DISCOVERABLE;
+       *supported |=  BTP_GAP_SETTING_BONDABLE;
+       *supported |=  BTP_GAP_SETTING_SSP;
+       *supported |=  BTP_GAP_SETTING_BREDR;
+       *supported |=  BTP_GAP_SETTING_LE;
+       *supported |=  BTP_GAP_SETTING_ADVERTISING;
+       *supported |=  BTP_GAP_SETTING_SC;
+       *supported |=  BTP_GAP_SETTING_PRIVACY;
+       /* *supported |=  BTP_GAP_SETTING_STATIC_ADDRESS; */
+
+       /* TODO not all info is availbe via D-Bus API so some are assumed to be
+        * enabled by bluetoothd or simply hardcoded until API is extended
+        */
+       *current |=  BTP_GAP_SETTING_CONNECTABLE;
+       *current |=  BTP_GAP_SETTING_SSP;
+       *current |=  BTP_GAP_SETTING_BREDR;
+       *current |=  BTP_GAP_SETTING_LE;
+       *current |=  BTP_GAP_SETTING_PRIVACY;
+       *current |=  BTP_GAP_SETTING_SC;
+       /* *supported |=  BTP_GAP_SETTING_STATIC_ADDRESS; */
+
+       if (l_dbus_proxy_get_property(proxy, "Powered", "b", &prop) && prop)
+               *current |=  BTP_GAP_SETTING_POWERED;
+
+       if (l_dbus_proxy_get_property(proxy, "Discoverable", "b", &prop) &&
+                                                                       prop)
+               *current |=  BTP_GAP_SETTING_DISCOVERABLE;
+
+       if (l_dbus_proxy_get_property(proxy, "Pairable", "b", &prop) && prop)
+               *current |=  BTP_GAP_SETTING_BONDABLE;
+}
+
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
+{
+       const char *interface = l_dbus_proxy_get_interface(proxy);
+       const char *path = l_dbus_proxy_get_path(proxy);
+
+       l_info("Proxy added: %s (%s)", interface, path);
+
+       if (!strcmp(interface, "org.bluez.Adapter1")) {
+               struct btp_adapter *adapter;
+
+               adapter = l_new(struct btp_adapter, 1);
+               adapter->proxy = proxy;
+               adapter->index = l_queue_length(adapters);
+               adapter->devices = l_queue_new();
+
+               extract_settings(proxy, &adapter->current_settings,
+                                               &adapter->supported_settings);
+
+               l_queue_push_tail(adapters, adapter);
+               return;
+       }
+
+       if (!strcmp(interface, "org.bluez.Device1")) {
+               struct btp_adapter *adapter;
+               struct btp_device *device;
+               char *str;
+
+               if (!l_dbus_proxy_get_property(proxy, "Adapter", "o", &str))
+                       return;
+
+               adapter = find_adapter_by_path(str);
+               if (!adapter)
+                       return;
+
+               device = l_new(struct btp_device, 1);
+               device->proxy = proxy;
+
+               l_queue_push_tail(adapter->devices, device);
+               return;
+       }
+}
+
+static bool device_match_by_proxy(const void *a, const void *b)
+{
+       const struct btp_device *device = a;
+       const struct l_dbus_proxy *proxy = b;
+
+       return device->proxy == proxy;
+}
+
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
+{
+       const char *interface = l_dbus_proxy_get_interface(proxy);
+       const char *path = l_dbus_proxy_get_path(proxy);
+
+       l_info("Proxy removed: %s (%s)", interface, path);
+
+       if (!strcmp(interface, "org.bluez.Adapter1")) {
+               l_info("Adapter removed, terminating.");
+               l_main_quit();
+               return;
+       }
+
+       if (!strcmp(interface, "org.bluez.Device1")) {
+               struct btp_adapter *adapter;
+               char *str;
+
+               if (!l_dbus_proxy_get_property(proxy, "Adapter", "o", &str))
+                       return;
+
+               adapter = find_adapter_by_path(str);
+               if (!adapter)
+                       return;
+
+               l_queue_remove_if(adapter->devices, device_match_by_proxy,
+                                                                       proxy);
+
+               return;
+       }
+}
+
+static void update_current_settings(struct btp_adapter *adapter,
+                                                       uint32_t new_settings)
+{
+       struct btp_new_settings_ev ev;
+
+       adapter->current_settings = new_settings;
+
+       ev.current_settings = L_CPU_TO_LE32(adapter->current_settings);
+
+       btp_send(btp, BTP_GAP_SERVICE, BTP_EV_GAP_NEW_SETTINGS, adapter->index,
+                                                       sizeof(ev), &ev);
+}
+
+static void property_changed(struct l_dbus_proxy *proxy, const char *name,
+                               struct l_dbus_message *msg, void *user_data)
+{
+       const char *interface = l_dbus_proxy_get_interface(proxy);
+       const char *path = l_dbus_proxy_get_path(proxy);
+
+       l_info("property_changed %s %s %s", name, path, interface);
+
+       if (!strcmp(interface, "org.bluez.Adapter1")) {
+               struct btp_adapter *adapter = find_adapter_by_proxy(proxy);
+               uint32_t new_settings;
+
+               if (!adapter)
+                       return;
+
+               new_settings = adapter->current_settings;
+
+               if (!strcmp(name, "Powered")) {
+                       bool prop;
+
+                       if (!l_dbus_message_get_arguments(msg, "b", &prop))
+                               return;
+
+                       if (prop)
+                               new_settings |= BTP_GAP_SETTING_POWERED;
+                       else
+                               new_settings &= ~BTP_GAP_SETTING_POWERED;
+               } else if (!strcmp(name, "Discoverable")) {
+                       bool prop;
+
+                       if (!l_dbus_message_get_arguments(msg, "b", &prop))
+                               return;
+
+                       if (prop)
+                               new_settings |= BTP_GAP_SETTING_DISCOVERABLE;
+                       else
+                               new_settings &= ~BTP_GAP_SETTING_DISCOVERABLE;
+               }
+
+               if (!strcmp(name, "Pairable")) {
+                       bool prop;
+
+                       if (!l_dbus_message_get_arguments(msg, "b", &prop))
+                               return;
+
+                       if (prop)
+                               new_settings |= BTP_GAP_SETTING_BONDABLE;
+                       else
+                               new_settings &= ~BTP_GAP_SETTING_BONDABLE;
+               }
+
+               if (new_settings != adapter->current_settings)
+                       update_current_settings(adapter, new_settings);
+
+               return;
+       }
+}
+
+static void client_connected(struct l_dbus *dbus, void *user_data)
+{
+       l_info("D-Bus client connected");
+}
+
+static void client_disconnected(struct l_dbus *dbus, void *user_data)
+{
+       l_info("D-Bus client disconnected, terminated");
+       l_main_quit();
+}
+
+static void client_ready(struct l_dbus_client *client, void *user_data)
+{
+       l_info("D-Bus client ready, connecting BTP");
+
+       btp = btp_new(socket_path);
+       if (!btp) {
+               l_error("Failed to connect BTP, terminating");
+               l_main_quit();
+               return;
+       }
+
+       register_core_service();
+
+       btp_send(btp, BTP_CORE_SERVICE, BTP_EV_CORE_READY,
+                                       BTP_INDEX_NON_CONTROLLER, 0, NULL);
+}
+
+static void usage(void)
+{
+       l_info("btpclient - Bluetooth tester");
+       l_info("Usage:");
+       l_info("\tbtpclient [options]");
+       l_info("options:\n"
+       "\t-s, --socket <socket>  Socket to use for BTP\n"
+       "\t-q, --quiet            Don't emit any logs\n"
+       "\t-v, --version          Show version\n"
+       "\t-h, --help             Show help options");
+}
+
+static const struct option options[] = {
+       { "socket",     1, 0, 's' },
+       { "quiet",      0, 0, 'q' },
+       { "version",    0, 0, 'v' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct l_dbus_client *client;
+       struct l_signal *signal;
+       struct l_dbus *dbus;
+       sigset_t mask;
+       int opt;
+
+       l_log_set_stderr();
+
+       while ((opt = getopt_long(argc, argv, "+hs:vq", options, NULL)) != -1) {
+               switch (opt) {
+               case 's':
+                       socket_path = l_strdup(optarg);
+                       break;
+               case 'q':
+                       l_log_set_null();
+                       break;
+               case 'd':
+                       break;
+               case 'v':
+                       l_info("%s", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+               default:
+                       usage();
+                       return EXIT_SUCCESS;
+               }
+       }
+
+       if (!socket_path) {
+               l_info("Socket option is required");
+               l_info("Type --help for usage");
+               return EXIT_FAILURE;
+       }
+
+       if (!l_main_init())
+               return EXIT_FAILURE;
+
+
+       adapters = l_queue_new();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+       signal = l_signal_create(&mask, signal_handler, NULL, NULL);
+
+       dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+       client = l_dbus_client_new(dbus, "org.bluez", "/org/bluez");
+
+       l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
+       l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
+                                                                       NULL);
+
+       l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+                                               property_changed, NULL, NULL);
+
+       l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
+
+       l_main_run();
+
+       l_dbus_client_destroy(client);
+       l_dbus_destroy(dbus);
+       l_signal_remove(signal);
+       btp_cleanup(btp);
+
+       l_queue_destroy(adapters, (l_queue_destroy_func_t)btp_adapter_free);
+
+       l_free(socket_path);
+
+       l_main_exit();
+
+       return EXIT_SUCCESS;
+}
index 4bb7fef..ae0ff74 100755 (executable)
@@ -64,6 +64,7 @@ static uint16_t hci_index = 0;
 static bool client_active = false;
 static bool debug_enabled = false;
 static bool emulate_ecc = false;
+static bool skip_first_zero = false;
 
 static void hexdump_print(const char *str, void *user_data)
 {
@@ -76,6 +77,7 @@ struct proxy {
        uint8_t host_buf[4096];
        uint16_t host_len;
        bool host_shutdown;
+       bool host_skip_first_zero;
 
        /* Receive events, ACL and SCO data */
        int dev_fd;
@@ -323,6 +325,16 @@ static void host_read_callback(int fd, uint32_t events, void *user_data)
                util_hexdump('>', proxy->host_buf + proxy->host_len, len,
                                                hexdump_print, "H: ");
 
+       if (proxy->host_skip_first_zero && len > 0) {
+               proxy->host_skip_first_zero = false;
+               if (proxy->host_buf[proxy->host_len] == '\0') {
+                       printf("Skipping initial zero byte\n");
+                       len--;
+                       memmove(proxy->host_buf + proxy->host_len,
+                               proxy->host_buf + proxy->host_len + 1, len);
+               }
+       }
+
        proxy->host_len += len;
 
 process_packet:
@@ -502,6 +514,7 @@ static bool setup_proxy(int host_fd, bool host_shutdown,
 
        proxy->host_fd = host_fd;
        proxy->host_shutdown = host_shutdown;
+       proxy->host_skip_first_zero = skip_first_zero;
 
        proxy->dev_fd = dev_fd;
        proxy->dev_shutdown = dev_shutdown;
@@ -597,8 +610,15 @@ static void server_callback(int fd, uint32_t events, void *user_data)
 static int open_unix(const char *path)
 {
        struct sockaddr_un addr;
+       size_t len;
        int fd;
 
+       len = strlen(path);
+       if (len > sizeof(addr.sun_path) - 1) {
+               fprintf(stderr, "Path too long\n");
+               return -1;
+       }
+
        unlink(path);
 
        fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
@@ -609,7 +629,7 @@ static int open_unix(const char *path)
 
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
-       strcpy(addr.sun_path, path);
+       strncpy(addr.sun_path, path, len);
 
        if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                perror("Failed to bind Unix server socket");
@@ -765,7 +785,7 @@ int main(int argc, char *argv[])
        for (;;) {
                int opt;
 
-               opt = getopt_long(argc, argv, "rc:l::u::p:i:aedvh",
+               opt = getopt_long(argc, argv, "rc:l::u::p:i:aezdvh",
                                                main_options, NULL);
                if (opt < 0)
                        break;
@@ -784,9 +804,16 @@ int main(int argc, char *argv[])
                                server_address = "0.0.0.0";
                        break;
                case 'u':
-                       if (optarg)
+                       if (optarg) {
+                               struct sockaddr_un addr;
+
                                unix_path = optarg;
-                       else
+                               if (strlen(unix_path) >
+                                               sizeof(addr.sun_path) - 1) {
+                                       fprintf(stderr, "Path too long\n");
+                                       return EXIT_FAILURE;
+                               }
+                       } else
                                unix_path = "/tmp/bt-server-bredr";
                        break;
                case 'p':
@@ -809,6 +836,9 @@ int main(int argc, char *argv[])
                case 'e':
                        emulate_ecc = true;
                        break;
+               case 'z':
+                       skip_first_zero = true;
+                       break;
                case 'd':
                        debug_enabled = true;
                        break;
index 15ae7c4..9408fb6 100755 (executable)
@@ -567,7 +567,7 @@ char *csr_buildidtostr(uint16_t id)
                if (csr_map[i].id == id)
                        return csr_map[i].str;
 
-       snprintf(str, 11, "Build %d", id);
+       snprintf(str, sizeof(str), "Build %d", id);
        return str;
 }
 
index 9569ee0..f412c90 100755 (executable)
@@ -149,7 +149,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size,
 
        cmd.data[0] = 0x02;             /* Field length */
        cmd.data[1] = 0x01;             /* Flags */
-       cmd.data[2] |= 0x04;            /* BR/EDR Not Supported */
+       cmd.data[2] = 0x04;             /* BR/EDR Not Supported */
 
        cmd.data[3] = 0x03;             /* Field length */
        cmd.data[4] = 0x03;             /* 16-bit Service UUID list */
diff --git a/tools/gatt-example b/tools/gatt-example
deleted file mode 100755 (executable)
index a6f5cbe..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-#!/usr/bin/python
-
-import dbus
-import dbus.exceptions
-import dbus.mainloop.glib
-import dbus.service
-
-import array
-import gobject
-
-from random import randint
-
-mainloop = None
-
-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'
-
-GATT_SERVICE_IFACE = 'org.bluez.GattService1'
-GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
-GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
-
-class InvalidArgsException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
-
-class NotSupportedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.NotSupported'
-
-class NotPermittedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.NotPermitted'
-
-class InvalidValueLengthException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
-
-class FailedException(dbus.exceptions.DBusException):
-    _dbus_error_name = 'org.bluez.Error.Failed'
-
-
-class Service(dbus.service.Object):
-    PATH_BASE = '/org/bluez/example/service'
-
-    def __init__(self, bus, index, uuid, primary):
-        self.path = self.PATH_BASE + str(index)
-        self.bus = bus
-        self.uuid = uuid
-        self.primary = primary
-        self.characteristics = []
-        dbus.service.Object.__init__(self, bus, self.path)
-
-    def get_properties(self):
-        return {
-                GATT_SERVICE_IFACE: {
-                        'UUID': self.uuid,
-                        'Primary': self.primary,
-                        'Characteristics': dbus.Array(
-                                self.get_characteristic_paths(),
-                                signature='o')
-                }
-        }
-
-    def get_path(self):
-        return dbus.ObjectPath(self.path)
-
-    def add_characteristic(self, characteristic):
-        self.characteristics.append(characteristic)
-
-    def get_characteristic_paths(self):
-        result = []
-        for chrc in self.characteristics:
-            result.append(chrc.get_path())
-        return result
-
-    def get_characteristics(self):
-        return self.characteristics
-
-    @dbus.service.method(DBUS_PROP_IFACE,
-                         in_signature='s',
-                         out_signature='a{sv}')
-    def GetAll(self, interface):
-        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
-
-
-class Characteristic(dbus.service.Object):
-    def __init__(self, bus, index, uuid, flags, service):
-        self.path = service.path + '/char' + str(index)
-        self.bus = bus
-        self.uuid = uuid
-        self.service = service
-        self.flags = flags
-        self.descriptors = []
-        dbus.service.Object.__init__(self, bus, self.path)
-
-    def get_properties(self):
-        return {
-                GATT_CHRC_IFACE: {
-                        'Service': self.service.get_path(),
-                        'UUID': self.uuid,
-                        'Flags': self.flags,
-                        'Descriptors': dbus.Array(
-                                self.get_descriptor_paths(),
-                                signature='o')
-                }
-        }
-
-    def get_path(self):
-        return dbus.ObjectPath(self.path)
-
-    def add_descriptor(self, descriptor):
-        self.descriptors.append(descriptor)
-
-    def get_descriptor_paths(self):
-        result = []
-        for desc in self.descriptors:
-            result.append(desc.get_path())
-        return result
-
-    def get_descriptors(self):
-        return self.descriptors
-
-    @dbus.service.method(DBUS_PROP_IFACE,
-                         in_signature='s',
-                         out_signature='a{sv}')
-    def GetAll(self, interface):
-        if interface != GATT_CHRC_IFACE:
-            raise InvalidArgsException()
-
-        return self.get_properties[GATT_CHRC_IFACE]
-
-    @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
-    def ReadValue(self):
-        print 'Default ReadValue called, returning error'
-        raise NotSupportedException()
-
-    @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
-        print 'Default WriteValue called, returning error'
-        raise NotSupportedException()
-
-    @dbus.service.method(GATT_CHRC_IFACE)
-    def StartNotify(self):
-        print 'Default StartNotify called, returning error'
-        raise NotSupportedException()
-
-    @dbus.service.method(GATT_CHRC_IFACE)
-    def StopNotify(self):
-        print 'Default StopNotify called, returning error'
-        raise NotSupportedException()
-
-    @dbus.service.signal(DBUS_PROP_IFACE,
-                         signature='sa{sv}as')
-    def PropertiesChanged(self, interface, changed, invalidated):
-        pass
-
-
-class Descriptor(dbus.service.Object):
-    def __init__(self, bus, index, uuid, characteristic):
-        self.path = characteristic.path + '/desc' + str(index)
-        self.bus = bus
-        self.uuid = uuid
-        self.chrc = characteristic
-        dbus.service.Object.__init__(self, bus, self.path)
-
-    def get_properties(self):
-        return {
-                GATT_DESC_IFACE: {
-                        'Characteristic': self.chrc.get_path(),
-                        'UUID': self.uuid,
-                }
-        }
-
-    def get_path(self):
-        return dbus.ObjectPath(self.path)
-
-    @dbus.service.method(DBUS_PROP_IFACE,
-                         in_signature='s',
-                         out_signature='a{sv}')
-    def GetAll(self, interface):
-        if interface != GATT_DESC_IFACE:
-            raise InvalidArgsException()
-
-        return self.get_properties[GATT_CHRC_IFACE]
-
-    @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
-    def ReadValue(self):
-        print 'Default ReadValue called, returning error'
-        raise NotSupportedException()
-
-    @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
-    def WriteValue(self, value):
-        print 'Default WriteValue called, returning error'
-        raise NotSupportedException()
-
-
-class HeartRateService(Service):
-    """
-    Fake Heart Rate Service that simulates a fake heart beat and control point
-    behavior.
-
-    """
-    HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
-
-    def __init__(self, bus, index):
-        Service.__init__(self, bus, index, self.HR_UUID, True)
-        self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
-        self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
-        self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
-        self.energy_expended = 0
-
-
-class HeartRateMeasurementChrc(Characteristic):
-    HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
-
-    def __init__(self, bus, index, service):
-        Characteristic.__init__(
-                self, bus, index,
-                self.HR_MSRMT_UUID,
-                ['notify'],
-                service)
-        self.notifying = False
-        self.hr_ee_count = 0
-
-    def hr_msrmt_cb(self):
-        value = []
-        value.append(dbus.Byte(0x06))
-
-        value.append(dbus.Byte(randint(90, 130)))
-
-        if self.hr_ee_count % 10 == 0:
-            value[0] = dbus.Byte(value[0] | 0x08)
-            value.append(dbus.Byte(self.service.energy_expended & 0xff))
-            value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
-
-        self.service.energy_expended = \
-                min(0xffff, self.service.energy_expended + 1)
-        self.hr_ee_count += 1
-
-        print 'Updating value: ' + repr(value)
-
-        self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
-
-        return self.notifying
-
-    def _update_hr_msrmt_simulation(self):
-        print 'Update HR Measurement Simulation'
-
-        if not self.notifying:
-            return
-
-        gobject.timeout_add(1000, self.hr_msrmt_cb)
-
-    def StartNotify(self):
-        if self.notifying:
-            print 'Already notifying, nothing to do'
-            return
-
-        self.notifying = True
-        self._update_hr_msrmt_simulation()
-
-    def StopNotify(self):
-        if not self.notifying:
-            print 'Not notifying, nothing to do'
-            return
-
-        self.notifying = False
-        self._update_hr_msrmt_simulation()
-
-
-class BodySensorLocationChrc(Characteristic):
-    BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
-
-    def __init__(self, bus, index, service):
-        Characteristic.__init__(
-                self, bus, index,
-                self.BODY_SNSR_LOC_UUID,
-                ['read'],
-                service)
-
-    def ReadValue(self):
-        # Return 'Chest' as the sensor location.
-        return [ 0x01 ]
-
-class HeartRateControlPointChrc(Characteristic):
-    HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
-
-    def __init__(self, bus, index, service):
-        Characteristic.__init__(
-                self, bus, index,
-                self.HR_CTRL_PT_UUID,
-                ['write'],
-                service)
-
-    def WriteValue(self, value):
-        print 'Heart Rate Control Point WriteValue called'
-
-        if len(value) != 1:
-            raise InvalidValueLengthException()
-
-        byte = value[0]
-        print 'Control Point value: ' + repr(byte)
-
-        if byte != 1:
-            raise FailedException("0x80")
-
-        print 'Energy Expended field reset!'
-        self.service.energy_expended = 0
-
-
-class BatteryService(Service):
-    """
-    Fake Battery service that emulates a draining battery.
-
-    """
-    BATTERY_UUID = '180f'
-
-    def __init__(self, bus, index):
-        Service.__init__(self, bus, index, self.BATTERY_UUID, True)
-        self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
-
-
-class BatteryLevelCharacteristic(Characteristic):
-    """
-    Fake Battery Level characteristic. The battery level is drained by 2 points
-    every 5 seconds.
-
-    """
-    BATTERY_LVL_UUID = '2a19'
-
-    def __init__(self, bus, index, service):
-        Characteristic.__init__(
-                self, bus, index,
-                self.BATTERY_LVL_UUID,
-                ['read', 'notify'],
-                service)
-        self.notifying = False
-        self.battery_lvl = 100
-        gobject.timeout_add(5000, self.drain_battery)
-
-    def notify_battery_level(self):
-        if not self.notifying:
-            return
-        self.PropertiesChanged(
-                GATT_CHRC_IFACE,
-                { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
-
-    def drain_battery(self):
-        if self.battery_lvl > 0:
-            self.battery_lvl -= 2
-            if self.battery_lvl < 0:
-                self.battery_lvl = 0
-        print 'Battery Level drained: ' + repr(self.battery_lvl)
-        self.notify_battery_level()
-        return True
-
-    def ReadValue(self):
-        print 'Battery Level read: ' + repr(self.battery_lvl)
-        return [dbus.Byte(self.battery_lvl)]
-
-    def StartNotify(self):
-        if self.notifying:
-            print 'Already notifying, nothing to do'
-            return
-
-        self.notifying = True
-        self.notify_battery_level()
-
-    def StopNotify(self):
-        if not self.notifying:
-            print 'Not notifying, nothing to do'
-            return
-
-        self.notifying = False
-
-
-class TestService(Service):
-    """
-    Dummy test service that provides characteristics and descriptors that
-    exercise various API functionality.
-
-    """
-    TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
-
-    def __init__(self, bus, index):
-        Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
-        self.add_characteristic(TestCharacteristic(bus, 0, self))
-
-
-class TestCharacteristic(Characteristic):
-    """
-    Dummy test characteristic. Allows writing arbitrary bytes to its value, and
-    contains "extended properties", as well as a test descriptor.
-
-    """
-    TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
-
-    def __init__(self, bus, index, service):
-        Characteristic.__init__(
-                self, bus, index,
-                self.TEST_CHRC_UUID,
-                ['read', 'write', 'writable-auxiliaries'],
-                service)
-        self.value = []
-        self.add_descriptor(TestDescriptor(bus, 0, self))
-        self.add_descriptor(
-                CharacteristicUserDescriptionDescriptor(bus, 1, self))
-
-    def ReadValue(self):
-        print 'TestCharacteristic Read: ' + repr(self.value)
-        return self.value
-
-    def WriteValue(self, value):
-        print 'TestCharacteristic Write: ' + repr(value)
-        self.value = value
-
-
-class TestDescriptor(Descriptor):
-    """
-    Dummy test descriptor. Returns a static value.
-
-    """
-    TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
-
-    def __init__(self, bus, index, characteristic):
-        Descriptor.__init__(
-                self, bus, index,
-                self.TEST_DESC_UUID,
-                characteristic)
-
-    def ReadValue(self):
-        return [
-                dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
-        ]
-
-
-class CharacteristicUserDescriptionDescriptor(Descriptor):
-    """
-    Writable CUD descriptor.
-
-    """
-    CUD_UUID = '2901'
-
-    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 = self.value.tolist()
-        Descriptor.__init__(
-                self, bus, index,
-                self.CUD_UUID,
-                characteristic)
-
-    def ReadValue(self):
-        return self.value
-
-    def WriteValue(self, value):
-        if not self.writable:
-            raise NotPermittedException()
-        self.value = value
-
-
-def register_service_cb():
-    print 'GATT service registered'
-
-
-def register_service_error_cb(error):
-    print 'Failed to register service: ' + str(error)
-    mainloop.quit()
-
-
-def find_adapter(bus):
-    remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
-                               DBUS_OM_IFACE)
-    objects = remote_om.GetManagedObjects()
-
-    for o, props in objects.iteritems():
-        if props.has_key(GATT_MANAGER_IFACE):
-            return o
-
-    return None
-
-def main():
-    global mainloop
-
-    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-
-    bus = dbus.SystemBus()
-
-    adapter = find_adapter(bus)
-    if not adapter:
-        print 'GattManager1 interface not found'
-        return
-
-    service_manager = dbus.Interface(
-            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)
-
-    mainloop.run()
-
-if __name__ == '__main__':
-    main()
index 75d09f0..e900789 100755 (executable)
@@ -451,7 +451,7 @@ static void test_le_read_local_pk_status(const void *data, uint8_t size,
        uint8_t status = *((uint8_t *) data);
 
        if (status) {
-               tester_warn("Failed to send DHKey gen cmd (0x%02x)", status);
+               tester_warn("Failed to send Read Local PK256 cmd (0x%02x)", status);
                tester_test_failed();
                return;
        }
index 474545a..85a2e08 100755 (executable)
@@ -120,7 +120,7 @@ static int load_file(int dd, uint16_t version, const char *suffix)
 {
        DIR *dir;
        struct dirent *d;
-       char pathname[PATH_MAX], filename[NAME_MAX], prefix[20];
+       char pathname[PATH_MAX], filename[NAME_MAX + 2], prefix[20];
        unsigned char cmd[256];
        unsigned char buf[256];
        uint8_t seqnum = 0;
index 9d48e66..4b35804 100755 (executable)
@@ -149,8 +149,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size,
 
        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[2] = 0x04;             /* BR/EDR Not Supported */
 
        cmd.data[3] = 0x1a;             /* Field length */
        cmd.data[4] = 0xff;             /* Vendor field */
index f57b195..1078d18 100755 (executable)
@@ -329,7 +329,7 @@ static void test_condition_complete(struct test_data *data)
                user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
                user->test_setup = setup; \
                user->test_data = data; \
-               user->expected_version = 0x08; \
+               user->expected_version = 0x09; \
                user->expected_manufacturer = 0x003f; \
                user->expected_supported_settings = 0x0000bfff; \
                user->initial_settings = 0x00000080; \
@@ -380,7 +380,7 @@ static void test_condition_complete(struct test_data *data)
                user->hciemu_type = HCIEMU_TYPE_LE; \
                user->test_setup = setup; \
                user->test_data = data; \
-               user->expected_version = 0x08; \
+               user->expected_version = 0x09; \
                user->expected_manufacturer = 0x003f; \
                user->expected_supported_settings = 0x0000be1b; \
                user->initial_settings = 0x00000200; \
@@ -5001,7 +5001,7 @@ static const struct generic_data read_local_oob_success_sc_test = {
 
 static const char ext_ctrl_info1[] = {
        0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
-       0x08, /* version */
+       0x09, /* version */
        0x3f, 0x00, /* manufacturer */
        0xff, 0xbf, 0x00, 0x00, /* supported settings */
        0x80, 0x00, 0x00, 0x00, /* current settings */
@@ -5046,7 +5046,7 @@ static const struct setup_mgmt_cmd set_dev_class_cmd_arr1[] = {
 
 static const char ext_ctrl_info2[] = {
        0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
-       0x08, /* version */
+       0x09, /* version */
        0x3f, 0x00, /* manufacturer */
        0xff, 0xbf, 0x00, 0x00, /* supported settings */
        0x81, 0x02, 0x00, 0x00, /* current settings */
@@ -5077,7 +5077,7 @@ static const struct generic_data read_ext_ctrl_info2 = {
 
 static const char ext_ctrl_info3[] = {
        0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
-       0x08, /* version */
+       0x09, /* version */
        0x3f, 0x00, /* manufacturer */
        0xff, 0xbf, 0x00, 0x00, /* supported settings */
        0x80, 0x02, 0x00, 0x00, /* current settings */
@@ -5112,7 +5112,7 @@ static const struct generic_data read_ext_ctrl_info3 = {
 
 static const char ext_ctrl_info4[] = {
        0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
-       0x08, /* version */
+       0x09, /* version */
        0x3f, 0x00, /* manufacturer */
        0xff, 0xbf, 0x00, 0x00, /* supported settings */
        0x80, 0x02, 0x00, 0x00, /* current settings */
@@ -5171,7 +5171,7 @@ static const struct setup_mgmt_cmd set_dev_class_cmd_arr2[] = {
 
 static const char ext_ctrl_info5[] = {
        0x00, 0x00, 0x00, 0x01, 0xaa, 0x00, /* btaddr */
-       0x08, /* version */
+       0x09, /* version */
        0x3f, 0x00, /* manufacturer */
        0xff, 0xbf, 0x00, 0x00, /* supported settings */
        0x81, 0x02, 0x00, 0x00, /* current settings */
index d0ba8a6..1e3e6f0 100755 (executable)
@@ -242,7 +242,8 @@ static void parse_line(char *line_read)
                return;
        }
 
-       add_history(line_read);
+       if (history_search(line_read, -1))
+               add_history(line_read);
 
        g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
 
index 892fcb3..209442c 100755 (executable)
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <signal.h>
-#include <sys/signalfd.h>
+#include <string.h>
 #include <inttypes.h>
 #include <wordexp.h>
+#include <ctype.h>
 
-#include <readline/readline.h>
-#include <readline/history.h>
 #include <glib.h>
 
 #include "gdbus/gdbus.h"
-#include "client/display.h"
+#include "src/shared/shell.h"
 
 /* String display constants */
 #define COLORED_NEW    COLOR_GREEN "NEW" COLOR_OFF
@@ -59,7 +57,6 @@
 #define OBEX_MAP_INTERFACE "org.bluez.obex.MessageAccess1"
 #define OBEX_MSG_INTERFACE "org.bluez.obex.Message1"
 
-static GMainLoop *main_loop;
 static DBusConnection *dbus_conn;
 static GDBusProxy *default_session;
 static GSList *sessions = NULL;
@@ -78,23 +75,12 @@ struct transfer_data {
 
 static void connect_handler(DBusConnection *connection, void *user_data)
 {
-       rl_set_prompt(PROMPT_ON);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
+       bt_shell_set_prompt(PROMPT_ON);
 }
 
 static void disconnect_handler(DBusConnection *connection, void *user_data)
 {
-       rl_set_prompt(PROMPT_OFF);
-       printf("\r");
-       rl_on_new_line();
-       rl_redisplay();
-}
-
-static void cmd_quit(int argc, char *argv[])
-{
-       g_main_loop_quit(main_loop);
+       bt_shell_set_prompt(PROMPT_OFF);
 }
 
 static void connect_reply(DBusMessage *message, void *user_data)
@@ -104,12 +90,12 @@ static void connect_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to connect: %s\n", error.name);
+               bt_shell_printf("Failed to connect: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Connection successful\n");
+       bt_shell_printf("Connection successful\n");
 }
 
 static void append_variant(DBusMessageIter *iter, int type, void *val)
@@ -187,13 +173,8 @@ static void cmd_connect(int argc, char *argv[])
        struct connect_args *args;
        const char *target = "opp";
 
-       if (argc < 2) {
-               rl_printf("Missing device address argument\n");
-               return;
-       }
-
        if (!client) {
-               rl_printf("Client proxy not available\n");
+               bt_shell_printf("Client proxy not available\n");
                return;
        }
 
@@ -206,11 +187,11 @@ static void cmd_connect(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(client, "CreateSession", connect_setup,
                        connect_reply, args, connect_args_free) == FALSE) {
-               rl_printf("Failed to connect\n");
+               bt_shell_printf("Failed to connect\n");
                return;
        }
 
-       rl_printf("Attempting to connect to %s\n", argv[1]);
+       bt_shell_printf("Attempting to connect to %s\n", argv[1]);
 }
 
 static void disconnect_reply(DBusMessage *message, void *user_data)
@@ -220,12 +201,12 @@ static void disconnect_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to disconnect: %s\n", error.name);
+               bt_shell_printf("Failed to disconnect: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Disconnection successful\n");
+       bt_shell_printf("Disconnection successful\n");
 }
 
 static void disconnect_setup(DBusMessageIter *iter, void *user_data)
@@ -262,17 +243,17 @@ static void cmd_disconnect(int argc, char *argv[])
                proxy = default_session;
 
        if (proxy == NULL) {
-               rl_printf("Session not available\n");
+               bt_shell_printf("Session not available\n");
                return;
        }
 
        if (g_dbus_proxy_method_call(client, "RemoveSession", disconnect_setup,
                                disconnect_reply, proxy, NULL) == FALSE) {
-               rl_printf("Failed to disconnect\n");
+               bt_shell_printf("Failed to disconnect\n");
                return;
        }
 
-       rl_printf("Attempting to disconnect to %s\n",
+       bt_shell_printf("Attempting to disconnect to %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -297,7 +278,7 @@ static void print_proxy(GDBusProxy *proxy, const char *title,
 
        str = proxy_description(proxy, title, description);
 
-       rl_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
+       bt_shell_printf("%s%s\n", str, default_session == proxy ? "[default]" : "");
 
        g_free(str);
 }
@@ -315,7 +296,7 @@ static void cmd_list(int argc, char *arg[])
 static bool check_default_session(void)
 {
        if (!default_session) {
-               rl_printf("No default session available\n");
+               bt_shell_printf("No default session available\n");
                return FALSE;
        }
 
@@ -334,39 +315,39 @@ static void print_iter(const char *label, const char *name,
        DBusMessageIter subiter;
 
        if (iter == NULL) {
-               rl_printf("%s%s is nil\n", label, name);
+               bt_shell_printf("%s%s is nil\n", label, name);
                return;
        }
 
        switch (dbus_message_iter_get_arg_type(iter)) {
        case DBUS_TYPE_INVALID:
-               rl_printf("%s%s is invalid\n", label, name);
+               bt_shell_printf("%s%s is invalid\n", label, name);
                break;
        case DBUS_TYPE_STRING:
        case DBUS_TYPE_OBJECT_PATH:
                dbus_message_iter_get_basic(iter, &valstr);
-               rl_printf("%s%s: %s\n", label, name, valstr);
+               bt_shell_printf("%s%s: %s\n", label, name, valstr);
                break;
        case DBUS_TYPE_BOOLEAN:
                dbus_message_iter_get_basic(iter, &valbool);
-               rl_printf("%s%s: %s\n", label, name,
+               bt_shell_printf("%s%s: %s\n", label, name,
                                        valbool == TRUE ? "yes" : "no");
                break;
        case DBUS_TYPE_UINT64:
                dbus_message_iter_get_basic(iter, &valu64);
-               rl_printf("%s%s: %" PRIu64 "\n", label, name, valu64);
+               bt_shell_printf("%s%s: %" PRIu64 "\n", label, name, valu64);
                break;
        case DBUS_TYPE_UINT32:
                dbus_message_iter_get_basic(iter, &valu32);
-               rl_printf("%s%s: 0x%08x\n", label, name, valu32);
+               bt_shell_printf("%s%s: 0x%08x\n", label, name, valu32);
                break;
        case DBUS_TYPE_UINT16:
                dbus_message_iter_get_basic(iter, &valu16);
-               rl_printf("%s%s: 0x%04x\n", label, name, valu16);
+               bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
                break;
        case DBUS_TYPE_INT16:
                dbus_message_iter_get_basic(iter, &vals16);
-               rl_printf("%s%s: %d\n", label, name, vals16);
+               bt_shell_printf("%s%s: %d\n", label, name, vals16);
                break;
        case DBUS_TYPE_VARIANT:
                dbus_message_iter_recurse(iter, &subiter);
@@ -387,7 +368,7 @@ static void print_iter(const char *label, const char *name,
                print_iter(label, valstr, &subiter);
                break;
        default:
-               rl_printf("%s%s has unsupported type\n", label, name);
+               bt_shell_printf("%s%s has unsupported type\n", label, name);
                break;
        }
 }
@@ -414,12 +395,12 @@ static void cmd_show(int argc, char *argv[])
        } else {
                proxy = find_session(argv[1]);
                if (!proxy) {
-                       rl_printf("Session %s not available\n", argv[1]);
+                       bt_shell_printf("Session %s not available\n", argv[1]);
                        return;
                }
        }
 
-       rl_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Session %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(proxy, "Destination");
        print_property(proxy, "Target");
@@ -441,8 +422,7 @@ static void set_default_session(GDBusProxy *proxy)
        desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
 
 done:
-       rl_set_prompt(desc);
-       rl_redisplay();
+       bt_shell_set_prompt(desc);
        g_free(desc);
 }
 
@@ -450,14 +430,9 @@ static void cmd_select(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing session address argument\n");
-               return;
-       }
-
        proxy = find_session(argv[1]);
        if (proxy == NULL) {
-               rl_printf("Session %s not available\n", argv[1]);
+               bt_shell_printf("Session %s not available\n", argv[1]);
                return;
        }
 
@@ -499,7 +474,7 @@ static GDBusProxy *find_message(const char *path)
 
 static void transfer_info(GDBusProxy *proxy, int argc, char *argv[])
 {
-       rl_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Transfer %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(proxy, "Session");
        print_property(proxy, "Name");
@@ -513,7 +488,7 @@ static void transfer_info(GDBusProxy *proxy, int argc, char *argv[])
 
 static void message_info(GDBusProxy *proxy, int argc, char *argv[])
 {
-       rl_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
+       bt_shell_printf("Message %s\n", g_dbus_proxy_get_path(proxy));
 
        print_property(proxy, "Folder");
        print_property(proxy, "Subject");
@@ -537,11 +512,6 @@ static void cmd_info(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing object path argument\n");
-               return;
-       }
-
        proxy = find_transfer(argv[1]);
        if (proxy) {
                transfer_info(proxy, argc, argv);
@@ -554,7 +524,7 @@ static void cmd_info(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Object %s not available\n", argv[1]);
+       bt_shell_printf("Object %s not available\n", argv[1]);
 }
 
 static void cancel_reply(DBusMessage *message, void *user_data)
@@ -564,36 +534,31 @@ static void cancel_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to cancel: %s\n", error.name);
+               bt_shell_printf("Failed to cancel: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Cancel successful\n");
+       bt_shell_printf("Cancel successful\n");
 }
 
 static void cmd_cancel(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing transfer address argument\n");
-               return;
-       }
-
        proxy = find_transfer(argv[1]);
        if (!proxy) {
-               rl_printf("Transfer %s not available\n", argv[1]);
+               bt_shell_printf("Transfer %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "Cancel", NULL, cancel_reply, NULL,
                                                        NULL) == FALSE) {
-               rl_printf("Failed to cancel transfer\n");
+               bt_shell_printf("Failed to cancel transfer\n");
                return;
        }
 
-       rl_printf("Attempting to cancel transfer %s\n",
+       bt_shell_printf("Attempting to cancel transfer %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -604,36 +569,31 @@ static void suspend_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to suspend: %s\n", error.name);
+               bt_shell_printf("Failed to suspend: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Suspend successful\n");
+       bt_shell_printf("Suspend successful\n");
 }
 
 static void cmd_suspend(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing transfer address argument\n");
-               return;
-       }
-
        proxy = find_transfer(argv[1]);
        if (!proxy) {
-               rl_printf("Transfer %s not available\n", argv[1]);
+               bt_shell_printf("Transfer %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply,
                                                NULL, NULL) == FALSE) {
-               rl_printf("Failed to suspend transfer\n");
+               bt_shell_printf("Failed to suspend transfer\n");
                return;
        }
 
-       rl_printf("Attempting to suspend transfer %s\n",
+       bt_shell_printf("Attempting to suspend transfer %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -644,36 +604,31 @@ static void resume_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to resume: %s\n", error.name);
+               bt_shell_printf("Failed to resume: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Resume successful\n");
+       bt_shell_printf("Resume successful\n");
 }
 
 static void cmd_resume(int argc, char *argv[])
 {
        GDBusProxy *proxy;
 
-       if (argc < 2) {
-               rl_printf("Missing transfer address argument\n");
-               return;
-       }
-
        proxy = find_transfer(argv[1]);
        if (!proxy) {
-               rl_printf("Transfer %s not available\n", argv[1]);
+               bt_shell_printf("Transfer %s not available\n", argv[1]);
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply,
                                                NULL, NULL) == FALSE) {
-               rl_printf("Failed to resume transfer\n");
+               bt_shell_printf("Failed to resume transfer\n");
                return;
        }
 
-       rl_printf("Attempting to resume transfer %s\n",
+       bt_shell_printf("Attempting to resume transfer %s\n",
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -743,7 +698,7 @@ static void print_transfer_iter(DBusMessageIter *iter)
 
        dbus_message_iter_get_basic(iter, &path);
 
-       rl_printf("Transfer %s\n", path);
+       bt_shell_printf("Transfer %s\n", path);
 
        dbus_message_iter_next(iter);
 
@@ -758,7 +713,7 @@ static void send_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to send/pull: %s\n", error.name);
+               bt_shell_printf("Failed to send/pull: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -777,35 +732,25 @@ static void send_setup(DBusMessageIter *iter, void *user_data)
 
 static void opp_send(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing file argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "SendFile", send_setup, send_reply,
                                        g_strdup(argv[1]), g_free) == FALSE) {
-               rl_printf("Failed to send\n");
+               bt_shell_printf("Failed to send\n");
                return;
        }
 
-       rl_printf("Attempting to send %s to %s\n", argv[1],
+       bt_shell_printf("Attempting to send %s to %s\n", argv[1],
                                                g_dbus_proxy_get_path(proxy));
 }
 
 static void opp_pull(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing file argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup,
                        send_reply, g_strdup(argv[1]), g_free) == FALSE) {
-               rl_printf("Failed to pull\n");
+               bt_shell_printf("Failed to pull\n");
                return;
        }
 
-       rl_printf("Attempting to pull %s from %s\n", argv[1],
+       bt_shell_printf("Attempting to pull %s from %s\n", argv[1],
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -817,7 +762,7 @@ static void push_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to PushMessage: %s\n", error.name);
+               bt_shell_printf("Failed to PushMessage: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -848,19 +793,14 @@ static void push_setup(DBusMessageIter *iter, void *user_data)
 
 static void map_send(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing file argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "PushMessage", push_setup,
                                        push_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to send\n");
+               bt_shell_printf("Failed to send\n");
                return;
        }
 
-       rl_printf("Attempting to send %s to %s\n", argv[1],
+       bt_shell_printf("Attempting to send %s to %s\n", argv[1],
                                                g_dbus_proxy_get_path(proxy));
 }
 
@@ -883,7 +823,7 @@ static void cmd_send(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 static void cmd_pull(int argc, char *argv[])
@@ -899,7 +839,7 @@ static void cmd_pull(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 static void change_folder_reply(DBusMessage *message, void *user_data)
@@ -909,12 +849,12 @@ static void change_folder_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to ChangeFolder: %s\n", error.name);
+               bt_shell_printf("Failed to ChangeFolder: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("ChangeFolder successful\n");
+       bt_shell_printf("ChangeFolder successful\n");
 }
 
 static void change_folder_setup(DBusMessageIter *iter, void *user_data)
@@ -932,14 +872,14 @@ static void select_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to Select: %s\n", error.name);
+               bt_shell_printf("Failed to Select: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
        dbus_message_iter_init(message, &iter);
 
-       rl_printf("Select successful\n");
+       bt_shell_printf("Select successful\n");
 }
 
 static void select_setup(DBusMessageIter *iter, void *user_data)
@@ -958,12 +898,12 @@ static void setfolder_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to SetFolder: %s\n", error.name);
+               bt_shell_printf("Failed to SetFolder: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("SetFolder successful\n");
+       bt_shell_printf("SetFolder successful\n");
 }
 
 static void setfolder_setup(DBusMessageIter *iter, void *user_data)
@@ -1003,53 +943,38 @@ static GDBusProxy *find_pbap(const char *path)
 
 static void ftp_cd(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing path argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "ChangeFolder", change_folder_setup,
                                        change_folder_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to ChangeFolder\n");
+               bt_shell_printf("Failed to ChangeFolder\n");
                return;
        }
 
-       rl_printf("Attempting to ChangeFolder to %s\n", argv[1]);
+       bt_shell_printf("Attempting to ChangeFolder to %s\n", argv[1]);
 }
 
 static void pbap_cd(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing path argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "Select", select_setup,
                                        select_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to Select\n");
+               bt_shell_printf("Failed to Select\n");
                return;
        }
 
-       rl_printf("Attempting to Select to %s\n", argv[1]);
+       bt_shell_printf("Attempting to Select to %s\n", argv[1]);
 }
 
 static void map_cd(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing path argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "SetFolder", setfolder_setup,
                                        setfolder_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to SetFolder\n");
+               bt_shell_printf("Failed to SetFolder\n");
                return;
        }
 
-       rl_printf("Attempting to SetFolder to %s\n", argv[1]);
+       bt_shell_printf("Attempting to SetFolder to %s\n", argv[1]);
 }
 
 static void cmd_cd(int argc, char *argv[])
@@ -1077,7 +1002,7 @@ static void cmd_cd(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 static void list_folder_reply(DBusMessage *message, void *user_data)
@@ -1088,7 +1013,7 @@ static void list_folder_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to ListFolder: %s\n", error.name);
+               bt_shell_printf("Failed to ListFolder: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1111,11 +1036,11 @@ static void ftp_ls(GDBusProxy *proxy, int argc, char *argv[])
        if (g_dbus_proxy_method_call(proxy, "ListFolder", NULL,
                                                list_folder_reply, NULL,
                                                NULL) == FALSE) {
-               rl_printf("Failed to ls\n");
+               bt_shell_printf("Failed to ls\n");
                return;
        }
 
-       rl_printf("Attempting to ListFolder\n");
+       bt_shell_printf("Attempting to ListFolder\n");
 }
 
 static void parse_list_reply(DBusMessage *message)
@@ -1152,7 +1077,7 @@ static void list_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to List: %s\n", error.name);
+               bt_shell_printf("Failed to List: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1181,7 +1106,7 @@ static void search_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to Search: %s\n", error.name);
+               bt_shell_printf("Failed to Search: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1215,11 +1140,11 @@ static void pbap_search(GDBusProxy *proxy, int argc, char *argv[])
        if (g_dbus_proxy_method_call(proxy, "Search", search_setup,
                                        search_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to Search\n");
+               bt_shell_printf("Failed to Search\n");
                return;
        }
 
-       rl_printf("Attempting to Search\n");
+       bt_shell_printf("Attempting to Search\n");
 }
 
 static void list_folders_reply(DBusMessage *message, void *user_data)
@@ -1230,7 +1155,7 @@ static void list_folders_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to ListFolders: %s\n", error.name);
+               bt_shell_printf("Failed to ListFolders: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1270,7 +1195,7 @@ static void list_messages_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to ListFolders: %s\n", error.name);
+               bt_shell_printf("Failed to ListFolders: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1289,7 +1214,7 @@ static void list_messages_reply(DBusMessage *message, void *user_data)
 
                dbus_message_iter_recurse(&array, &entry);
                dbus_message_iter_get_basic(&entry, &obj);
-               rl_printf("\t%s\n", obj);
+               bt_shell_printf("\t%s\n", obj);
                dbus_message_iter_next(&array);
        }
 }
@@ -1318,11 +1243,11 @@ static void pbap_list(GDBusProxy *proxy, int argc, char *argv[])
 {
        if (g_dbus_proxy_method_call(proxy, "List", list_setup, list_reply,
                                                NULL, NULL) == FALSE) {
-               rl_printf("Failed to List\n");
+               bt_shell_printf("Failed to List\n");
                return;
        }
 
-       rl_printf("Attempting to List\n");
+       bt_shell_printf("Attempting to List\n");
 }
 
 static void get_size_reply(DBusMessage *message, void *user_data)
@@ -1334,7 +1259,7 @@ static void get_size_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to GetSize: %s\n", error.name);
+               bt_shell_printf("Failed to GetSize: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1350,11 +1275,11 @@ static void pbap_get_size(GDBusProxy *proxy, int argc, char *argv[])
 {
        if (g_dbus_proxy_method_call(proxy, "GetSize", NULL, get_size_reply,
                                                proxy, NULL) == FALSE) {
-               rl_printf("Failed to GetSize\n");
+               bt_shell_printf("Failed to GetSize\n");
                return;
        }
 
-       rl_printf("Attempting to GetSize\n");
+       bt_shell_printf("Attempting to GetSize\n");
 }
 
 static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
@@ -1375,11 +1300,11 @@ static void map_ls_messages(GDBusProxy *proxy, int argc, char *argv[])
        if (g_dbus_proxy_method_call(proxy, "ListMessages", list_messages_setup,
                                        list_messages_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to ListMessages\n");
+               bt_shell_printf("Failed to ListMessages\n");
                return;
        }
 
-       rl_printf("Attempting to ListMessages\n");
+       bt_shell_printf("Attempting to ListMessages\n");
 }
 
 static void map_ls(GDBusProxy *proxy, int argc, char *argv[])
@@ -1392,11 +1317,11 @@ static void map_ls(GDBusProxy *proxy, int argc, char *argv[])
        if (g_dbus_proxy_method_call(proxy, "ListFolders", list_folders_setup,
                                                list_folders_reply, NULL,
                                                NULL) == FALSE) {
-               rl_printf("Failed to ListFolders\n");
+               bt_shell_printf("Failed to ListFolders\n");
                return;
        }
 
-       rl_printf("Attempting to ListFolders\n");
+       bt_shell_printf("Attempting to ListFolders\n");
 }
 
 static void cmd_ls(int argc, char *argv[])
@@ -1424,7 +1349,7 @@ static void cmd_ls(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 struct cp_args {
@@ -1481,12 +1406,12 @@ static void copy_file_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to CopyFile: %s\n", error.name);
+               bt_shell_printf("Failed to CopyFile: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("CopyFile successful\n");
+       bt_shell_printf("CopyFile successful\n");
 }
 
 static void ftp_copy(GDBusProxy *proxy, int argc, char *argv[])
@@ -1497,11 +1422,11 @@ static void ftp_copy(GDBusProxy *proxy, int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "CopyFile", cp_setup,
                                copy_file_reply, args, cp_free) == FALSE) {
-               rl_printf("Failed to CopyFile\n");
+               bt_shell_printf("Failed to CopyFile\n");
                return;
        }
 
-       rl_printf("Attempting to CopyFile\n");
+       bt_shell_printf("Attempting to CopyFile\n");
 }
 
 static void get_file_reply(DBusMessage *message, void *user_data)
@@ -1512,7 +1437,7 @@ static void get_file_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to GetFile: %s\n", error.name);
+               bt_shell_printf("Failed to GetFile: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1541,11 +1466,11 @@ static void ftp_get(GDBusProxy *proxy, int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "GetFile", get_file_setup,
                                get_file_reply, args, cp_free) == FALSE) {
-               rl_printf("Failed to GetFile\n");
+               bt_shell_printf("Failed to GetFile\n");
                return;
        }
 
-       rl_printf("Attempting to GetFile\n");
+       bt_shell_printf("Attempting to GetFile\n");
 }
 
 static void put_file_reply(DBusMessage *message, void *user_data)
@@ -1556,7 +1481,7 @@ static void put_file_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to PutFile: %s\n", error.name);
+               bt_shell_printf("Failed to PutFile: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1571,7 +1496,7 @@ static void ftp_put(GDBusProxy *proxy, int argc, char *argv[])
        struct cp_args *args;
 
        if (rindex(argv[2], ':') != NULL) {
-               rl_printf("Invalid target file argument\n");
+               bt_shell_printf("Invalid target file argument\n");
                return;
        }
 
@@ -1579,25 +1504,15 @@ static void ftp_put(GDBusProxy *proxy, int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "PutFile", cp_setup, put_file_reply,
                                                args, cp_free) == FALSE) {
-               rl_printf("Failed to PutFile\n");
+               bt_shell_printf("Failed to PutFile\n");
                return;
        }
 
-       rl_printf("Attempting to PutFile\n");
+       bt_shell_printf("Attempting to PutFile\n");
 }
 
 static void ftp_cp(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing source file argument\n");
-               return;
-       }
-
-       if (argc < 3) {
-               rl_printf("Missing target file argument\n");
-               return;
-       }
-
        if (rindex(argv[1], ':') == NULL)
                return ftp_get(proxy, argc, argv);
 
@@ -1611,13 +1526,13 @@ static void pull_all_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to PullAll: %s\n", error.name);
+               bt_shell_printf("Failed to PullAll: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
 
-       rl_printf("PullAll successful\n");
+       bt_shell_printf("PullAll successful\n");
 }
 
 static void pull_all_setup(DBusMessageIter *iter, void *user_data)
@@ -1642,11 +1557,11 @@ static void pbap_pull_all(GDBusProxy *proxy, int argc, char *argv[])
        if (g_dbus_proxy_method_call(proxy, "PullAll", pull_all_setup,
                                        pull_all_reply, g_strdup(argv[2]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to PullAll\n");
+               bt_shell_printf("Failed to PullAll\n");
                return;
        }
 
-       rl_printf("Attempting to PullAll\n");
+       bt_shell_printf("Attempting to PullAll\n");
 }
 
 static void pull_reply(DBusMessage *message, void *user_data)
@@ -1656,13 +1571,13 @@ static void pull_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to Pull: %s\n", error.name);
+               bt_shell_printf("Failed to Pull: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
 
-       rl_printf("Pull successful\n");
+       bt_shell_printf("Pull successful\n");
 }
 
 static void pull_setup(DBusMessageIter *iter, void *user_data)
@@ -1691,26 +1606,16 @@ static void pbap_pull(GDBusProxy *proxy, int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "Pull", pull_setup, pull_reply,
                                                args, cp_free) == FALSE) {
-               rl_printf("Failed to Pull\n");
+               bt_shell_printf("Failed to Pull\n");
                return;
        }
 
-       rl_printf("Attempting to Pull\n");
+       bt_shell_printf("Attempting to Pull\n");
 }
 
 static void pbap_cp(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing source file argument\n");
-               return;
-       }
-
-       if (argc < 3) {
-               rl_printf("Missing target file argument\n");
-               return;
-       }
-
-       if (strcmp(argv[1], "*") == 0)
+       if (strcmp(argv[1], "*") == 0 || strcmp(argv[1], "*.vcf") == 0)
                return pbap_pull_all(proxy, argc, argv);
 
        return pbap_pull(proxy, argc, argv);
@@ -1724,7 +1629,7 @@ static void get_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to Get: %s\n", error.name);
+               bt_shell_printf("Failed to Get: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
@@ -1747,29 +1652,19 @@ static void map_cp(GDBusProxy *proxy, int argc, char *argv[])
 {
        GDBusProxy *obj;
 
-       if (argc < 2) {
-               rl_printf("Missing message argument\n");
-               return;
-       }
-
        obj = find_message(argv[1]);
        if (obj == NULL) {
-               rl_printf("Invalid message argument\n");
-               return;
-       }
-
-       if (argc < 3) {
-               rl_printf("Missing target file argument\n");
+               bt_shell_printf("Invalid message argument\n");
                return;
        }
 
        if (g_dbus_proxy_method_call(obj, "Get", get_setup, get_reply,
                                        g_strdup(argv[2]), g_free) == FALSE) {
-               rl_printf("Failed to Get\n");
+               bt_shell_printf("Failed to Get\n");
                return;
        }
 
-       rl_printf("Attempting to Get\n");
+       bt_shell_printf("Attempting to Get\n");
 }
 
 static void cmd_cp(int argc, char *argv[])
@@ -1798,7 +1693,7 @@ static void cmd_cp(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 static void move_file_reply(DBusMessage *message, void *user_data)
@@ -1808,12 +1703,12 @@ static void move_file_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to MoveFile: %s\n", error.name);
+               bt_shell_printf("Failed to MoveFile: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("MoveFile successful\n");
+       bt_shell_printf("MoveFile successful\n");
 }
 
 static void cmd_mv(int argc, char *argv[])
@@ -1824,19 +1719,9 @@ static void cmd_mv(int argc, char *argv[])
        if (!check_default_session())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing source file argument\n");
-               return;
-       }
-
-       if (argc < 3) {
-               rl_printf("Missing target file argument\n");
-               return;
-       }
-
        proxy = find_ftp(g_dbus_proxy_get_path(default_session));
        if (proxy == NULL) {
-               rl_printf("Command not supported\n");
+               bt_shell_printf("Command not supported\n");
                return;
        }
 
@@ -1844,11 +1729,11 @@ static void cmd_mv(int argc, char *argv[])
 
        if (g_dbus_proxy_method_call(proxy, "MoveFile", cp_setup,
                                move_file_reply, args, cp_free) == FALSE) {
-               rl_printf("Failed to MoveFile\n");
+               bt_shell_printf("Failed to MoveFile\n");
                return;
        }
 
-       rl_printf("Attempting to MoveFile\n");
+       bt_shell_printf("Attempting to MoveFile\n");
 }
 
 static void delete_reply(DBusMessage *message, void *user_data)
@@ -1858,12 +1743,12 @@ static void delete_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to Delete: %s\n", error.name);
+               bt_shell_printf("Failed to Delete: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("Delete successful\n");
+       bt_shell_printf("Delete successful\n");
 }
 
 static void delete_setup(DBusMessageIter *iter, void *user_data)
@@ -1875,27 +1760,22 @@ static void delete_setup(DBusMessageIter *iter, void *user_data)
 
 static void ftp_rm(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc < 2) {
-               rl_printf("Missing file argument\n");
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "Delete", delete_setup,
                                        delete_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to Delete\n");
+               bt_shell_printf("Failed to Delete\n");
                return;
        }
 
-       rl_printf("Attempting to Delete\n");
+       bt_shell_printf("Attempting to Delete\n");
 }
 
 static void set_delete_reply(const DBusError *error, void *user_data)
 {
        if (dbus_error_is_set(error))
-               rl_printf("Failed to set Deleted: %s\n", error->name);
+               bt_shell_printf("Failed to set Deleted: %s\n", error->name);
        else
-               rl_printf("Set Deleted successful\n");
+               bt_shell_printf("Set Deleted successful\n");
 }
 
 static void map_rm(GDBusProxy *proxy, int argc, char *argv[])
@@ -1903,25 +1783,20 @@ static void map_rm(GDBusProxy *proxy, int argc, char *argv[])
        GDBusProxy *msg;
        dbus_bool_t value = TRUE;
 
-       if (argc < 2) {
-               rl_printf("Missing message argument\n");
-               return;
-       }
-
        msg = find_message(argv[1]);
        if (msg == NULL) {
-               rl_printf("Invalid message argument\n");
+               bt_shell_printf("Invalid message argument\n");
                return;
        }
 
        if (g_dbus_proxy_set_property_basic(msg, "Deleted", DBUS_TYPE_BOOLEAN,
                                                &value, set_delete_reply,
                                                NULL, NULL) == FALSE) {
-               rl_printf("Failed to set Deleted\n");
+               bt_shell_printf("Failed to set Deleted\n");
                return;
        }
 
-       rl_printf("Attempting to set Deleted\n");
+       bt_shell_printf("Attempting to set Deleted\n");
 }
 
 static void cmd_rm(int argc, char *argv[])
@@ -1943,7 +1818,7 @@ static void cmd_rm(int argc, char *argv[])
                return;
        }
 
-       rl_printf("Command not supported\n");
+       bt_shell_printf("Command not supported\n");
 }
 
 static void create_folder_reply(DBusMessage *message, void *user_data)
@@ -1953,12 +1828,12 @@ static void create_folder_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == TRUE) {
-               rl_printf("Failed to CreateFolder: %s\n", error.name);
+               bt_shell_printf("Failed to CreateFolder: %s\n", error.name);
                dbus_error_free(&error);
                return;
        }
 
-       rl_printf("CreateFolder successful\n");
+       bt_shell_printf("CreateFolder successful\n");
 }
 
 static void create_folder_setup(DBusMessageIter *iter, void *user_data)
@@ -1975,33 +1850,25 @@ static void cmd_mkdir(int argc, char *argv[])
        if (!check_default_session())
                return;
 
-       if (argc < 2) {
-               rl_printf("Missing folder argument\n");
-               return;
-       }
-
        proxy = find_ftp(g_dbus_proxy_get_path(default_session));
        if (proxy == NULL) {
-               rl_printf("Command not supported\n");
+               bt_shell_printf("Command not supported\n");
                return;
        }
 
        if (g_dbus_proxy_method_call(proxy, "CreateFolder", create_folder_setup,
                                        create_folder_reply, g_strdup(argv[1]),
                                        g_free) == FALSE) {
-               rl_printf("Failed to CreateFolder\n");
+               bt_shell_printf("Failed to CreateFolder\n");
                return;
        }
 
-       rl_printf("Attempting to CreateFolder\n");
+       bt_shell_printf("Attempting to CreateFolder\n");
 }
 
-static const struct {
-       const char *cmd;
-       const char *arg;
-       void (*func) (int argc, char *argv[]);
-       const char *desc;
-} cmd_table[] = {
+static const struct bt_shell_menu main_menu = {
+       .name = "main",
+       .entries = {
        { "connect",      "<dev> [uuid]", cmd_connect, "Connect session" },
        { "disconnect",   "[session]", cmd_disconnect, "Disconnect session" },
        { "list",         NULL,       cmd_list, "List available sessions" },
@@ -2022,220 +1889,9 @@ static const struct {
                                "Move source file to destination file" },
        { "rm",          "<file>",    cmd_rm, "Delete file" },
        { "mkdir",       "<folder>",    cmd_mkdir, "Create folder" },
-       { "quit",         NULL,       cmd_quit, "Quit program" },
-       { "exit",         NULL,       cmd_quit },
-       { "help" },
-       {}
+       {} },
 };
 
-static char *cmd_generator(const char *text, int state)
-{
-       static int index, len;
-       const char *cmd;
-
-       if (!state) {
-               index = 0;
-               len = strlen(text);
-       }
-
-       while ((cmd = cmd_table[index].cmd)) {
-               index++;
-
-               if (!strncmp(cmd, text, len))
-                       return strdup(cmd);
-       }
-
-       return NULL;
-}
-
-static char **cmd_completion(const char *text, int start, int end)
-{
-       char **matches = NULL;
-
-       if (start == 0) {
-               rl_completion_display_matches_hook = NULL;
-               matches = rl_completion_matches(text, cmd_generator);
-       }
-
-       if (!matches)
-               rl_attempted_completion_over = 1;
-
-       return matches;
-}
-
-static void rl_handler(char *input)
-{
-       wordexp_t w;
-       int argc;
-       char **argv;
-       int i;
-
-       if (!input) {
-               rl_insert_text("quit");
-               rl_redisplay();
-               rl_crlf();
-               g_main_loop_quit(main_loop);
-               return;
-       }
-
-       if (!strlen(input))
-               goto done;
-
-       add_history(input);
-
-       if (wordexp(input, &w, WRDE_NOCMD))
-               goto done;
-
-       if (w.we_wordc == 0)
-               goto free_we;
-
-       argv = w.we_wordv;
-       argc = w.we_wordc;
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (strcmp(argv[0], cmd_table[i].cmd))
-                       continue;
-
-               if (cmd_table[i].func) {
-                       cmd_table[i].func(argc, argv);
-                       goto free_we;
-               }
-       }
-
-       if (strcmp(argv[0], "help")) {
-               printf("Invalid command\n");
-               goto free_we;
-       }
-
-       printf("Available commands:\n");
-
-       for (i = 0; cmd_table[i].cmd; i++) {
-               if (cmd_table[i].desc)
-                       printf("  %s %-*s %s\n", cmd_table[i].cmd,
-                                       (int)(25 - strlen(cmd_table[i].cmd)),
-                                       cmd_table[i].arg ? : "",
-                                       cmd_table[i].desc ? : "");
-       }
-
-free_we:
-       wordfree(&w);
-done:
-       free(input);
-}
-
-static gboolean option_version = FALSE;
-
-static GOptionEntry options[] = {
-       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
-                               "Show version information and exit" },
-       { NULL },
-};
-
-static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
-{
-       static unsigned int __terminated = 0;
-       struct signalfd_siginfo si;
-       ssize_t result;
-       int fd;
-
-       if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               g_main_loop_quit(main_loop);
-               return FALSE;
-       }
-
-       fd = g_io_channel_unix_get_fd(channel);
-
-       result = read(fd, &si, sizeof(si));
-       if (result != sizeof(si))
-               return FALSE;
-
-       switch (si.ssi_signo) {
-       case SIGINT:
-               rl_replace_line("", 0);
-               rl_crlf();
-               rl_on_new_line();
-               rl_redisplay();
-               break;
-       case SIGTERM:
-               if (__terminated == 0) {
-                       rl_replace_line("", 0);
-                       rl_crlf();
-                       g_main_loop_quit(main_loop);
-               }
-
-               __terminated = 1;
-               break;
-       }
-
-       return TRUE;
-}
-
-static guint setup_signalfd(void)
-{
-       GIOChannel *channel;
-       guint source;
-       sigset_t mask;
-       int fd;
-
-       sigemptyset(&mask);
-       sigaddset(&mask, SIGINT);
-       sigaddset(&mask, SIGTERM);
-
-       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
-               perror("Failed to set signal mask");
-               return 0;
-       }
-
-       fd = signalfd(-1, &mask, 0);
-       if (fd < 0) {
-               perror("Failed to create signal descriptor");
-               return 0;
-       }
-
-       channel = g_io_channel_unix_new(fd);
-
-       g_io_channel_set_close_on_unref(channel, TRUE);
-       g_io_channel_set_encoding(channel, NULL, NULL);
-       g_io_channel_set_buffered(channel, FALSE);
-
-       source = g_io_add_watch(channel,
-                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               signal_handler, NULL);
-
-       g_io_channel_unref(channel);
-
-       return source;
-}
-
-static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
-                                                       gpointer user_data)
-{
-       if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
-               g_main_loop_quit(main_loop);
-               return FALSE;
-       }
-
-       rl_callback_read_char();
-       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 client_added(GDBusProxy *proxy)
 {
        if (client == NULL)
@@ -2266,16 +1922,17 @@ static void print_transferred(struct transfer_data *data, const char *str,
        data->transferred = valu64;
 
        if (data->size == 0) {
-               rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s)\n",
-                                               str, valu64, speed / 1000);
+               bt_shell_printf("%sTransferred: %" PRIu64 " (@%" PRIu64
+                               "KB/s)\n", str, valu64, speed / 1000);
                return;
        }
 
        seconds = (data->size - data->transferred) / speed;
        minutes = seconds / 60;
        seconds %= 60;
-       rl_printf("%sTransferred: %" PRIu64 " (@%" PRIu64 "KB/s %02u:%02u)\n",
-                               str, valu64, speed / 1000, minutes, seconds);
+       bt_shell_printf("%sTransferred: %" PRIu64 " (@%" PRIu64
+                       "KB/s %02u:%02u)\n", str, valu64, speed / 1000,
+                       minutes, seconds);
 }
 
 static void transfer_property_changed(GDBusProxy *proxy, const char *name,
@@ -2318,7 +1975,7 @@ static void transfer_added(GDBusProxy *proxy)
 
        data = g_new0(struct transfer_data, 1);
 
-       if (g_dbus_proxy_get_property(proxy, "Transfered", &iter))
+       if (g_dbus_proxy_get_property(proxy, "Transferred", &iter))
                dbus_message_iter_get_basic(&iter, &data->transferred);
 
        if (g_dbus_proxy_get_property(proxy, "Size", &iter))
@@ -2494,47 +2151,18 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 
 int main(int argc, char *argv[])
 {
-       GOptionContext *context;
-       GError *error = NULL;
        GDBusClient *client;
-       guint signal, input;
-
-       context = g_option_context_new(NULL);
-       g_option_context_add_main_entries(context, options, NULL);
-
-       if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
-               if (error != NULL) {
-                       g_printerr("%s\n", error->message);
-                       g_error_free(error);
-               } else
-                       g_printerr("An unknown error occurred\n");
-               exit(1);
-       }
-
-       g_option_context_free(context);
 
-       if (option_version == TRUE) {
-               printf("%s\n", VERSION);
-               exit(0);
-       }
+       bt_shell_init(argc, argv, NULL);
+       bt_shell_set_menu(&main_menu);
+       bt_shell_set_prompt(PROMPT_OFF);
+       bt_shell_attach(fileno(stdin));
 
-       main_loop = g_main_loop_new(NULL, FALSE);
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
 #else
        dbus_conn = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
 #endif
-
-       rl_attempted_completion_function = cmd_completion;
-
-       rl_erase_empty_line = 1;
-       rl_callback_handler_install(NULL, rl_handler);
-
-       rl_set_prompt(PROMPT_OFF);
-       rl_redisplay();
-
-       input = setup_standard_input();
-       signal = setup_signalfd();
        client = g_dbus_client_new(dbus_conn, "org.bluez.obex",
                                                        "/org/bluez/obex");
 
@@ -2544,17 +2172,11 @@ int main(int argc, char *argv[])
        g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
                                                        property_changed, NULL);
 
-       g_main_loop_run(main_loop);
+       bt_shell_run();
 
        g_dbus_client_unref(client);
-       g_source_remove(signal);
-       g_source_remove(input);
-
-       rl_message("");
-       rl_callback_handler_remove();
 
        dbus_connection_unref(dbus_conn);
-       g_main_loop_unref(main_loop);
 
        return 0;
 }
index 6dc358e..d5b2815 100755 (executable)
@@ -8,8 +8,14 @@ use strict;
 
 my %known_entities = (
     'nbsp' => ' ',
+    'aacute' => 'á',
     'eacute' => 'é',
+    'iacute' => 'í',
+    'oacute' => 'ó',
+    'uacute' => 'ú',
     'auml' => 'ä',
+    'uuml' => 'ü',
+    'Uuml' => 'Ü',
 );
 
 # better to use URI::Encode if you have it
@@ -21,8 +27,7 @@ sub uri_decode {
     }
     foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
         if ($entity ne 'amp') {
-            print "Unable to convert &$entity;, giving up\n";
-            exit 1;
+            die "\nparse_companies.pl: Unable to convert &$entity; giving up\n";
         }
     }
     $name =~ s/&amp;/&/ig;
@@ -45,10 +50,11 @@ while (<>) {
         $next_is_name = 1;
 
     # next <td> should be company name
-    } elsif ($next_is_name && m|\<td.*\>(.*)\</td\>|) {
+    } elsif ($next_is_name && m|\<td.*\>(.*)\<|) {
         my $name = uri_decode($1);
         $name =~ s/^\s+//g; # kill leading
         $name =~ s/\s+$//g; # and trailing space
+        $name =~ s/"/\\"/g; # escape double quotes
         my $id = hex($identifier);
         if ($id != 65535) {
             print "\tcase $id:\n";
index 4f53553..5397a42 100755 (executable)
@@ -504,6 +504,7 @@ static inline void get_configuration(int level, uint8_t hdr, struct frame *frm)
        switch (hdr & 0x03) {
        case 0x00:
                acp_seid(level, frm);
+               break;
        case 0x02:
                capabilities(level, frm);
                break;
@@ -571,6 +572,7 @@ static inline void security(int level, uint8_t hdr, struct frame *frm)
        switch (hdr & 0x03) {
        case 0x00:
                acp_seid(level, frm);
+               break;
        case 0x02:
                hex_dump(level + 1, frm, frm->len);
                frm->ptr += frm->len;
index 90b091e..c32519e 100755 (executable)
@@ -416,7 +416,7 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
 static const uint8_t smp_sc_req_1[] = {        0x01,   /* Pairing Request */
                                        0x03,   /* NoInputNoOutput */
                                        0x00,   /* OOB Flag */
-                                       0x09,   /* Bonding - no MITM, SC */
+                                       0x29,   /* Bonding - no MITM, SC, CT2 */
                                        0x10,   /* Max key size */
                                        0x0d,   /* Init. key dist. */
                                        0x0d,   /* Rsp. key dist. */
index 57df7fb..01001a8 100755 (executable)
@@ -419,6 +419,10 @@ static void create_dbus_system_conf(void)
 
        fclose(fp);
 
+       if (symlink("/etc/dbus-1/system.conf",
+                               "/usr/share/dbus-1/system.conf") < 0)
+               perror("Failed to create system.conf symlink");
+
        mkdir("/run/dbus", 0755);
 }
 
index 95c961d..e82d3e4 100755 (executable)
@@ -13,45 +13,30 @@ set -e -u
 tmpdir=$(mktemp -d)
 trap "rm -rf $tmpdir" EXIT
 
+scriptdir=$(pwd)
+
 mkdir $tmpdir/lib
 cp lib/bluetooth.c $tmpdir/lib/bluetooth.c.orig
 cp lib/bluetooth.c $tmpdir/lib/bluetooth.c
 
 cd $tmpdir
 
-path=en-us/specification/assigned-numbers/company-identifiers
-# Use "iconv -c" to strip unwanted unicode characters
-# Fixups:
-# - strip <input> tags of type "checkbox" because html2text generates UTF-8 for
-#   them in some distros even when using -ascii (e.g. Fedora)
-# - replace "&#160;" (non-breaking space) with whitespace manually, because
-#   some versions incorrectly convert it into "\xC2\xA0"
-curl https://www.bluetooth.org/$path | iconv -c -f utf8 -t ascii | \
-    sed '/<input.*type="checkbox"/d; s/&#160;/ /g' | \
-    html2text -ascii -width 160 -o identifiers.txt >/dev/null
-
-# Some versions of html2text do not replace &amp; (e.g. Fedora)
-sed -i 's/&amp;/\&/g' identifiers.txt
+echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
 
-sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
-    lib/bluetooth.c > old.c
+path=specifications/assigned-numbers/company-identifiers
+# Use "iconv -c" to strip unwanted unicode characters
+curl --insecure https://www.bluetooth.com/$path | \
+    $scriptdir/tools/parse_companies.pl >> new.c
 
-echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
-cat identifiers.txt |
-    perl -ne 'm/^(\d+)\s+0x[0-9a-f]+\s+(.*)/i &&
-        print "\tcase $1:\n\t\treturn \"$2\";\n"' >> new.c
 if ! grep -q "return \"" new.c; then
     echo "ERROR: could not parse company IDs from bluetooth.org" >&2
     exit 1
 fi
-if [ -n "$(tr -d '[:print:]\t\n' < new.c)" ]; then
-    echo -n "ERROR: invalid non-ASCII characters found while parsing" >&2
-    echo -n " company IDs. Please identify offending sequence and fix" >&2
-    echo " tools/update_compids.sh accordingly." >&2
-    exit 1
-fi
 echo -e '\tcase 65535:\n\t\treturn "internal use";' >> new.c
 echo -e '\tdefault:\n\t\treturn "not assigned";\n\t}\n}' >> new.c
 
+sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
+    lib/bluetooth.c > old.c
+
 diff -Naur old.c new.c | patch -sp0 lib/bluetooth.c
 diff -Naur lib/bluetooth.c.orig lib/bluetooth.c
index c7a8fa5..5d79e94 100755 (executable)
@@ -90,6 +90,11 @@ struct context {
                .size = sizeof(data(args)),                     \
        }
 
+#define false_pdu()                                            \
+       {                                                       \
+               .valid = false,                                 \
+       }
+
 #define define_test(name, function, type, bt_uuid, db,                 \
                test_step, args...)                                     \
        do {                                                            \
@@ -129,22 +134,18 @@ struct context {
                raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),                  \
                raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),      \
                raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0x08, 0x00, 0x02, 0x28),      \
                raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0x08, 0x00, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
                                0x2a),                                  \
-               raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),                  \
-               raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),                  \
-               raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),            \
-               raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x03, 0x00, 0x08, 0x00, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x06, 0x00, 0x0a, 0x07, 0x00, 0x29, \
                                0x2a),                                  \
                raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28),      \
                raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),                  \
+               raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),                  \
+               raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),            \
                raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00),                  \
                raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
 
@@ -158,22 +159,18 @@ struct context {
                raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),                  \
                raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),      \
                raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0x0a, 0x00, 0x02, 0x28),      \
                raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x02, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0x0a, 0x00, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
                                0x2a),                                  \
-               raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a),                  \
-               raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),                  \
-               raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),            \
-               raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x03, 0x00, 0x0a, 0x00, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x07, 0x00, 0x0a, 0x08, 0x00, 0x29, \
                                0x2a),                                  \
                raw_pdu(0x08, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x28),      \
                raw_pdu(0x01, 0x08, 0x08, 0x00, 0x0a),                  \
+               raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00),                  \
+               raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29),            \
                raw_pdu(0x04, 0x09, 0x00, 0x0a, 0x00),                  \
                raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
 
@@ -188,22 +185,18 @@ struct context {
                raw_pdu(0x01, 0x10, 0x21, 0x03, 0x0a),                  \
                raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28),      \
                raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a),                  \
-               raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x02, 0x28),      \
+               raw_pdu(0x08, 0x00, 0x01, 0x20, 0x03, 0x02, 0x28),      \
                raw_pdu(0x01, 0x08, 0x00, 0x01, 0x0a),                  \
-               raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x02, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x00, 0x03, 0x0a),                  \
-               raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x00, 0x01, 0x20, 0x03, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x10, 0x01, 0x02, 0x11, 0x01, 0x00, \
                        0x2a, 0x20, 0x01, 0x02, 0x21, 0x01, 0x01, 0x2a),\
-               raw_pdu(0x08, 0x21, 0x01, 0x21, 0x01, 0x03, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x21, 0x01, 0x0a),                  \
-               raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01),                  \
-               raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a),                  \
-               raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x21, 0x01, 0x20, 0x03, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x10, 0x03, 0x0a, 0x11, 0x03, 0x29, \
                        0x2a),                                          \
                raw_pdu(0x08, 0x11, 0x03, 0x20, 0x03, 0x03, 0x28),      \
                raw_pdu(0x01, 0x08, 0x11, 0x03, 0x0a),                  \
+               raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01),                  \
+               raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a),                  \
                raw_pdu(0x04, 0x12, 0x03, 0x20, 0x03),                  \
                raw_pdu(0x05, 0x01, 0x20, 0x03, 0x02, 0x29)
 
@@ -244,43 +237,39 @@ struct context {
                raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a)
 
 #define INCLUDE_DISC_SMALL_DB                                          \
-               raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x02, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),      \
                raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x0f, 0x00, \
                        0x0a, 0x18),                                    \
-               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)
+               raw_pdu(0x08, 0x12, 0xf0, 0xff, 0xff, 0x02, 0x28),      \
+               raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a)
 
 #define CHARACTERISTIC_DISC_SMALL_DB                                   \
-               raw_pdu(0x08, 0x10, 0xf0, 0x18, 0xf0, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x03, 0x28),      \
+               raw_pdu(0x09, 0x07, 0x02, 0x00, 0xb2, 0x03, 0x00, 0x29, \
+                       0x2a),                                          \
+               raw_pdu(0x08, 0x03, 0x00, 0xff, 0xff, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, \
                        0x2a),                                          \
-               raw_pdu(0x08, 0x13, 0xf0, 0x18, 0xf0, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x13, 0xf0, 0xff, 0xff, 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, 0x18, 0xf0, 0x03, 0x28),      \
+               raw_pdu(0x08, 0x15, 0xf0, 0xff, 0xff, 0x03, 0x28),      \
                raw_pdu(0x09, 0x07, 0x17, 0xf0, 0x02, 0x18, 0xf0, 0x01, \
                        0x2a),                                          \
-               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, 0xb2, 0x03, 0x00, 0x29, \
-                       0x2a),                                          \
-               raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28),      \
-               raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
+               raw_pdu(0x08, 0x18, 0xf0, 0xff, 0xff, 0x03, 0x28),      \
+               raw_pdu(0x01, 0x08, 0x18, 0xf0, 0x0a)
 
 #define DESCRIPTOR_DISC_SMALL_DB                                       \
                raw_pdu(0x04, 0x04, 0x00, 0x10, 0x00),                  \
                raw_pdu(0x05, 0x01, 0x04, 0x00, 0x02, 0x29, 0x05, 0x00, \
                        0x01, 0x29),                                    \
                raw_pdu(0x04, 0x06, 0x00, 0x10, 0x00),                  \
-               raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a)
+               raw_pdu(0x01, 0x04, 0x06, 0x00, 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)
 
 #define SMALL_DB_DISCOVERY_PDUS                                                \
                PRIMARY_DISC_SMALL_DB,                                  \
@@ -403,6 +392,13 @@ static gboolean send_pdu(gpointer user_data)
        if (pdu->valid && (pdu->size == 0)) {
                test_debug("(no action expected)", "GATT: ");
                context->pdu_offset++;
+
+               /* Quit the context if we processed the last PDU */
+               if (!context->data->pdu_list[context->pdu_offset].valid) {
+                       context_quit(context);
+                       return FALSE;
+               }
+
                return send_pdu(context);
        }
 
@@ -446,6 +442,8 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 
        util_hexdump('>', buf, len, test_debug, "GATT: ");
 
+       util_hexdump('=', pdu->data, pdu->size, test_debug, "PDU: ");
+
        g_assert_cmpint(len, ==, pdu->size);
 
        g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
@@ -686,8 +684,8 @@ static struct context *create_context(uint16_t mtu, gconstpointer data)
                bt_gatt_client_set_debug(context->client, print_debug,
                                                "bt_gatt_client:", NULL);
 
-               bt_gatt_client_set_ready_handler(context->client,
-                                               client_ready_cb, context, NULL);
+               bt_gatt_client_ready_register(context->client, client_ready_cb,
+                                                               context, NULL);
                break;
        default:
                break;
@@ -4461,5 +4459,17 @@ int main(int argc, char *argv[])
                        raw_pdu(0x18, 0x01),
                        raw_pdu(0x01, 0x18, 0x25, 0x00, 0x06));
 
+       define_test_server("/robustness/unkown-request",
+                       test_server, service_db_1, NULL,
+                       raw_pdu(0x03, 0x00, 0x02),
+                       raw_pdu(0xbf, 0x00),
+                       raw_pdu(0x01, 0xbf, 0x00, 0x00, 0x06));
+
+       define_test_server("/robustness/unkown-command",
+                       test_server, service_db_1, NULL,
+                       raw_pdu(0x03, 0x00, 0x02),
+                       raw_pdu(0xff, 0x00),
+                       raw_pdu());
+
        return tester_run();
 }
index 9f026e5..d117968 100755 (executable)
 
 #include <glib.h>
 
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+
 #include "src/shared/util.h"
 #include "src/shared/tester.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
 
 #include "attrib/gattrib.h"
 
diff --git a/unit/test-midi.c b/unit/test-midi.c
new file mode 100644 (file)
index 0000000..593bc5a
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2015,2016 Felipe F. Tonello <eu@felipetonello.com>
+ *  Copyright (C) 2016 ROLI Ltd.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#define NUM_WRITE_TESTS 100
+
+#include "src/shared/tester.h"
+#include "profiles/midi/libmidi.h"
+
+struct ble_midi_packet {
+       const uint8_t *data;
+       size_t size;
+};
+
+#define BLE_MIDI_PACKET_INIT(_packet) \
+       { \
+               .data = (_packet), \
+               .size = sizeof(_packet), \
+       }
+
+struct midi_read_test {
+       const struct ble_midi_packet *ble_packet;
+       size_t ble_packet_size;
+       const snd_seq_event_t *event;
+       size_t event_size;
+};
+
+#define BLE_READ_TEST_INIT(_ble_packet, _event) \
+       { \
+               .ble_packet = (_ble_packet), \
+               .ble_packet_size = G_N_ELEMENTS(_ble_packet), \
+               .event = (_event), \
+               .event_size = G_N_ELEMENTS(_event), \
+       }
+
+struct midi_write_test {
+       const snd_seq_event_t *event;
+       size_t event_size;
+       const snd_seq_event_t *event_expect;
+       size_t event_expect_size;
+};
+
+#define BLE_WRITE_TEST_INIT(_event, _event_expect)       \
+       { \
+               .event = (_event), \
+               .event_size = G_N_ELEMENTS(_event), \
+               .event_expect = (_event_expect), \
+               .event_expect_size = G_N_ELEMENTS(_event_expect), \
+       }
+
+#define BLE_WRITE_TEST_INIT_BASIC(_event) BLE_WRITE_TEST_INIT(_event, _event)
+
+#define NOTE_EVENT(_event, _channel, _note, _velocity) \
+       { \
+               .type = SND_SEQ_EVENT_##_event, \
+               .data = { \
+                       .note = { \
+                               .channel = (_channel), \
+                               .note = (_note), \
+                               .velocity = (_velocity), \
+                       }, \
+               }, \
+       }
+
+#define CONTROL_EVENT(_event, _channel, _value, _param) \
+       { \
+               .type = SND_SEQ_EVENT_##_event, \
+               .data = { \
+                       .control = { \
+                               .channel = (_channel), \
+                               .value = (_value), \
+                               .param = (_param), \
+                       }, \
+               }, \
+       }
+
+#define SYSEX_EVENT_RAW(_message, _size, _offset)        \
+       { \
+               .type = SND_SEQ_EVENT_SYSEX, \
+               .data = { \
+                       .ext = { \
+                               .ptr = (void *)(_message) + (_offset), \
+                               .len = (_size), \
+                       }, \
+               }, \
+       }
+
+#define SYSEX_EVENT(_message) SYSEX_EVENT_RAW(_message, sizeof(_message), 0)
+
+/* Multiple messages in one packet */
+static const uint8_t packet1_1[] = {
+       0xa6, 0x88, 0xe8, 0x00, 0x40, 0x88, 0xb8, 0x4a,
+       0x3f, 0x88, 0x98, 0x3e, 0x0e
+};
+
+/* Several one message per packet */
+static const uint8_t packet1_2[] = {
+       0xa6, 0xaa, 0xd8, 0x71
+};
+
+static const uint8_t packet1_3[] = {
+       0xa6, 0xb7, 0xb8, 0x4a, 0x43
+};
+
+/* This message contains a running status message */
+static const uint8_t packet1_4[] = {
+       0xa6, 0xc4, 0xe8, 0x7e, 0x3f, 0x7d, 0x3f, 0xc4,
+       0x7c, 0x3f
+};
+
+/* This message contain a running status message misplaced */
+static const uint8_t packet1_5[] = {
+       0xa6, 0xd9, 0x3e, 0x00, 0x88, 0x3e, 0x00
+};
+
+static const struct ble_midi_packet packet1[] = {
+       BLE_MIDI_PACKET_INIT(packet1_1),
+       BLE_MIDI_PACKET_INIT(packet1_2),
+       BLE_MIDI_PACKET_INIT(packet1_3),
+       BLE_MIDI_PACKET_INIT(packet1_4),
+       BLE_MIDI_PACKET_INIT(packet1_5),
+};
+
+static const snd_seq_event_t event1[] = {
+       CONTROL_EVENT(PITCHBEND, 8, 0, 0),    /* Pitch Bend */
+       CONTROL_EVENT(CONTROLLER, 8, 63, 74), /* Control Change */
+       NOTE_EVENT(NOTEON, 8, 62, 14),        /* Note On */
+       CONTROL_EVENT(CHANPRESS, 8, 113, 0),  /* Channel Aftertouch */
+       CONTROL_EVENT(CONTROLLER, 8, 67, 74), /* Control Change*/
+       CONTROL_EVENT(PITCHBEND, 8, -2, 0),   /* Pitch Bend */
+       CONTROL_EVENT(PITCHBEND, 8, -3, 0),   /* Pitch Bend */
+       CONTROL_EVENT(PITCHBEND, 8, -4, 0),   /* Pitch Bend */
+       NOTE_EVENT(NOTEOFF, 8, 62, 0),        /* Note Off */
+};
+
+static const struct midi_read_test midi1 = BLE_READ_TEST_INIT(packet1, event1);
+
+/* Basic SysEx in one packet */
+static const uint8_t packet2_1[] = {
+       0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0xda, 0xf7
+};
+
+/* SysEx across two packets */
+static const uint8_t packet2_2[] = {
+       0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05
+};
+
+static const uint8_t packet2_3[] = {
+       0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xdb, 0xf7
+};
+
+/* SysEx across multiple packets */
+static const uint8_t packet2_4[] = {
+       0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0x04, 0x05
+};
+
+static const uint8_t packet2_5[] = {
+       0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c
+};
+static const uint8_t packet2_6[] = {
+       0xa6, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13
+};
+
+static const uint8_t packet2_7[] = {
+       0xa6, 0x14, 0x15, 0x16, 0x17, 0x18, 0xdb, 0xf7
+};
+
+/* Two SysEx interleaved in two packets */
+static const uint8_t packet2_8[] = {
+       0xa6, 0xda, 0xf0, 0x01, 0x02, 0x03, 0xda, 0xf7,
+       0xda, 0xf0
+};
+
+static const uint8_t packet2_9[] = {
+       0xa6, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xdb, 0xf7
+};
+
+
+static const struct ble_midi_packet packet2[] = {
+       BLE_MIDI_PACKET_INIT(packet2_1),
+       BLE_MIDI_PACKET_INIT(packet2_2),
+       BLE_MIDI_PACKET_INIT(packet2_3),
+       BLE_MIDI_PACKET_INIT(packet2_4),
+       BLE_MIDI_PACKET_INIT(packet2_5),
+       BLE_MIDI_PACKET_INIT(packet2_6),
+       BLE_MIDI_PACKET_INIT(packet2_7),
+       BLE_MIDI_PACKET_INIT(packet2_8),
+       BLE_MIDI_PACKET_INIT(packet2_9),
+};
+
+static const uint8_t sysex2_1[] = {
+       0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex2_2[] = {
+       0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0xf7
+};
+
+static const uint8_t sysex2_3[] = {
+       0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+       0x18, 0xf7
+};
+
+static const uint8_t sysex2_4[] = {
+       0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex2_5[] = {
+       0xf0, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const snd_seq_event_t event2[] = {
+       SYSEX_EVENT(sysex2_1),
+       SYSEX_EVENT(sysex2_2),
+       SYSEX_EVENT(sysex2_3),
+       SYSEX_EVENT(sysex2_4),
+       SYSEX_EVENT(sysex2_5),
+};
+
+static const struct midi_read_test midi2 = BLE_READ_TEST_INIT(packet2, event2);
+
+static void compare_events(const snd_seq_event_t *ev1,
+                           const snd_seq_event_t *ev2)
+{
+       g_assert_cmpint(ev1->type, ==, ev2->type);
+
+       switch (ev1->type) {
+       case SND_SEQ_EVENT_NOTEON:
+       case SND_SEQ_EVENT_NOTEOFF:
+       case SND_SEQ_EVENT_KEYPRESS:
+               g_assert_cmpint(ev1->data.note.channel,
+                               ==,
+                               ev2->data.note.channel);
+               g_assert_cmpint(ev1->data.note.note,
+                               ==,
+                               ev2->data.note.note);
+               g_assert_cmpint(ev1->data.note.velocity,
+                               ==,
+                               ev2->data.note.velocity);
+               break;
+       case SND_SEQ_EVENT_CONTROLLER:
+               g_assert_cmpint(ev1->data.control.param,
+                               ==,
+                               ev2->data.control.param);
+               break;
+       case SND_SEQ_EVENT_PITCHBEND:
+       case SND_SEQ_EVENT_CHANPRESS:
+       case SND_SEQ_EVENT_PGMCHANGE:
+               g_assert_cmpint(ev1->data.control.channel,
+                               ==,
+                               ev2->data.control.channel);
+               g_assert_cmpint(ev1->data.control.value,
+                               ==,
+                               ev2->data.control.value);
+               break;
+       case SND_SEQ_EVENT_SYSEX:
+               g_assert_cmpint(ev1->data.ext.len,
+                               ==,
+                               ev2->data.ext.len);
+               g_assert(memcmp(ev1->data.ext.ptr,
+                               ev2->data.ext.ptr,
+                               ev2->data.ext.len) == 0);
+               break;
+       default:
+               g_assert_not_reached();
+       }
+}
+
+static void test_midi_reader(gconstpointer data)
+{
+       const struct midi_read_test *midi_test = data;
+       struct midi_read_parser midi;
+       int err;
+       size_t i; /* ble_packet counter */
+       size_t j; /* ble_packet length counter */
+       size_t k = 0; /* event counter */
+
+       err = midi_read_init(&midi);
+       g_assert_cmpint(err, ==, 0);
+
+       for (i = 0; i < midi_test->ble_packet_size; i++) {
+               const size_t length = midi_test->ble_packet[i].size;
+               j = 0;
+               midi_read_reset(&midi);
+               while (j < length) {
+                       snd_seq_event_t ev;
+                       const snd_seq_event_t *ev_expect = &midi_test->event[k];
+                       size_t count;
+
+                       g_assert_cmpint(k, <, midi_test->event_size);
+
+                       snd_seq_ev_clear(&ev);
+
+                       count = midi_read_raw(&midi,
+                                             midi_test->ble_packet[i].data + j,
+                                             length - j,
+                                             &ev);
+
+                       g_assert_cmpuint(count, >, 0);
+
+                       if (ev.type == SND_SEQ_EVENT_NONE)
+                               goto _continue_loop;
+                       else
+                               k++;
+
+                       compare_events(ev_expect, &ev);
+
+               _continue_loop:
+                       j += count;
+               }
+       }
+
+       midi_read_free(&midi);
+
+       tester_test_passed();
+}
+
+static const snd_seq_event_t event3[] = {
+       CONTROL_EVENT(PITCHBEND, 8, 0, 0),    /* Pitch Bend */
+       CONTROL_EVENT(CONTROLLER, 8, 63, 74), /* Control Change */
+       NOTE_EVENT(NOTEON, 8, 62, 14),        /* Note On */
+       CONTROL_EVENT(CHANPRESS, 8, 113, 0),  /* Channel Aftertouch */
+       CONTROL_EVENT(CONTROLLER, 8, 67, 74), /* Control Change*/
+       CONTROL_EVENT(PITCHBEND, 8, -2, 0),   /* Pitch Bend */
+       CONTROL_EVENT(PITCHBEND, 8, -3, 0),   /* Pitch Bend */
+       CONTROL_EVENT(PITCHBEND, 8, -4, 0),   /* Pitch Bend */
+       NOTE_EVENT(NOTEOFF, 8, 62, 0),        /* Note Off */
+};
+
+static const struct midi_write_test midi3 = BLE_WRITE_TEST_INIT_BASIC(event3);
+
+static const uint8_t sysex4_1[] = {
+       0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex4_2[] = {
+       0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0xf7
+};
+
+static const uint8_t sysex4_3[] = {
+       0xf0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+       0x18, 0xf7
+};
+
+static const uint8_t sysex4_4[] = {
+       0xf0, 0x01, 0x02, 0x03, 0xf7
+};
+
+static const uint8_t sysex4_5[] = {
+       0xf0, 0x06, 0x07, 0x08, 0x09, 0x0a, 0xf7
+};
+
+static const snd_seq_event_t event4[] = {
+       SYSEX_EVENT(sysex4_1),
+       SYSEX_EVENT(sysex4_2),
+       SYSEX_EVENT(sysex4_3),
+       SYSEX_EVENT(sysex4_4),
+       SYSEX_EVENT(sysex4_5),
+};
+
+static const struct midi_write_test midi4 = BLE_WRITE_TEST_INIT_BASIC(event4);
+
+/* Sysex split in multiple events (256 bytes each),
+   it's common for ALSA to split big sysexes into 256 bytes events */
+static const uint8_t sysex5_1[] = {
+       0xf0, 0x1b, 0x46, 0x52, 0x68, 0x45, 0x19, 0x3d,
+       0x70, 0x3c, 0x2b, 0x41, 0x09, 0x09, 0x28, 0x2d,
+       0x66, 0x00, 0x4f, 0x06, 0x22, 0x30, 0x77, 0x0d,
+       0x5d, 0x0e, 0x30, 0x21, 0x64, 0x5f, 0x72, 0x3c,
+       0x31, 0x3a, 0x03, 0x37, 0x3f, 0x00, 0x66, 0x52,
+       0x61, 0x6d, 0x03, 0x1e, 0x24, 0x2d, 0x33, 0x20,
+       0x69, 0x17, 0x77, 0x36, 0x58, 0x53, 0x11, 0x25,
+       0x2f, 0x51, 0x70, 0x4f, 0x00, 0x73, 0x6c, 0x3e,
+       0x66, 0x62, 0x53, 0x20, 0x0e, 0x41, 0x0b, 0x0e,
+       0x22, 0x27, 0x37, 0x14, 0x75, 0x31, 0x6f, 0x4b,
+       0x3e, 0x4b, 0x55, 0x71, 0x33, 0x2d, 0x0d, 0x3e,
+       0x58, 0x74, 0x4c, 0x44, 0x42, 0x2d, 0x47, 0x15,
+       0x3c, 0x75, 0x5f, 0x10, 0x26, 0x54, 0x5a, 0x1e,
+       0x07, 0x5a, 0x4a, 0x55, 0x55, 0x31, 0x40, 0x5d,
+       0x7f, 0x25, 0x32, 0x0c, 0x74, 0x1a, 0x05, 0x22,
+       0x66, 0x33, 0x5d, 0x38, 0x70, 0x15, 0x77, 0x35,
+       0x52, 0x09, 0x3e, 0x63, 0x76, 0x37, 0x3c, 0x25,
+       0x4c, 0x5c, 0x1e, 0x7d, 0x47, 0x0f, 0x2d, 0x67,
+       0x6e, 0x68, 0x3c, 0x4e, 0x08, 0x6c, 0x16, 0x58,
+       0x3d, 0x47, 0x19, 0x6d, 0x56, 0x12, 0x57, 0x56,
+       0x5e, 0x2d, 0x0d, 0x0d, 0x43, 0x25, 0x75, 0x70,
+       0x6a, 0x59, 0x3a, 0x7a, 0x41, 0x54, 0x03, 0x17,
+       0x2d, 0x7a, 0x67, 0x06, 0x78, 0x53, 0x0f, 0x43,
+       0x53, 0x4c, 0x02, 0x42, 0x68, 0x59, 0x0f, 0x51,
+       0x74, 0x40, 0x1d, 0x64, 0x6f, 0x46, 0x3f, 0x77,
+       0x71, 0x56, 0x2a, 0x24, 0x17, 0x25, 0x7f, 0x1f,
+       0x60, 0x19, 0x1d, 0x75, 0x4e, 0x43, 0x3d, 0x0d,
+       0x0d, 0x2e, 0x53, 0x44, 0x6c, 0x73, 0x7c, 0x6a,
+       0x12, 0x02, 0x11, 0x38, 0x6c, 0x2b, 0x2c, 0x5d,
+       0x4a, 0x48, 0x70, 0x7d, 0x44, 0x20, 0x41, 0x1e,
+       0x15, 0x4c, 0x43, 0x33, 0x1b, 0x7e, 0x43, 0x2f,
+       0x60, 0x6a, 0x61, 0x71, 0x21, 0x12, 0x32, 0x77,
+       0x3c, 0x21, 0x7a, 0x5f, 0x58, 0x6c, 0x1f, 0x3a,
+       0x68, 0x1c, 0x5d, 0x57, 0x1b, 0x0d, 0x77, 0x01,
+       0x10, 0x31, 0x4a, 0x73, 0x03, 0x48, 0x18, 0x0a,
+       0x32, 0x69, 0x38, 0x3f, 0x4d, 0x1a, 0x6e, 0x2f,
+       0x30, 0x56, 0x4c, 0x66, 0x76, 0x16, 0x3c, 0x7a,
+       0x31, 0x42, 0x40, 0x5d, 0x05, 0x33, 0x46, 0x53,
+       0x5f, 0x2c, 0x4d, 0x0d, 0x39, 0x53, 0x20, 0x6e,
+       0x61, 0x58, 0x12, 0x38, 0x25, 0x56, 0x22, 0x5b,
+       0x27, 0x44, 0x27, 0x44, 0x59, 0x16, 0x77, 0x26,
+       0x53, 0x35, 0x6e, 0x05, 0x70, 0x0f, 0x31, 0x30,
+       0x23, 0x2c, 0x65, 0x16, 0x2d, 0x05, 0x3e, 0x22,
+       0x6d, 0x22, 0x44, 0x3d, 0x18, 0x05, 0x10, 0x25,
+       0x6b, 0x66, 0x69, 0x14, 0x63, 0x63, 0x1b, 0x04,
+       0x41, 0x34, 0x6c, 0x09, 0x37, 0x6a, 0x63, 0x2e,
+       0x70, 0x72, 0x44, 0x41, 0x33, 0x01, 0x05, 0x05,
+       0x0b, 0x2a, 0x1a, 0x71, 0x55, 0x7e, 0x6e, 0x59,
+       0x47, 0x7d, 0x2f, 0x44, 0x03, 0x52, 0x6e, 0x6b,
+       0x4e, 0x11, 0x60, 0x1e, 0x0a, 0x71, 0x3d, 0x54,
+       0x02, 0x1c, 0x73, 0x0b, 0x76, 0x32, 0x48, 0x66,
+       0x36, 0x47, 0x6f, 0x5b, 0x6b, 0x3b, 0x14, 0x47,
+       0x0c, 0x16, 0x6c, 0x27, 0x2a, 0x73, 0x17, 0x1d,
+       0x16, 0x60, 0x63, 0x7b, 0x1d, 0x4f, 0x61, 0x5b,
+       0x13, 0x20, 0x46, 0x0c, 0x71, 0x7d, 0x27, 0x43,
+       0x49, 0x48, 0x7f, 0x3e, 0x4b, 0x7b, 0x27, 0x7b,
+       0x73, 0x53, 0x57, 0x68, 0x05, 0x2a, 0x2f, 0x36,
+       0x3b, 0x31, 0x11, 0x4e, 0x4c, 0x13, 0x2e, 0x06,
+       0x06, 0x7c, 0x40, 0x37, 0x27, 0x0f, 0x01, 0x67,
+       0x06, 0x09, 0x4b, 0x17, 0x0f, 0x4e, 0x51, 0x44,
+       0x66, 0x6c, 0x70, 0x2a, 0x55, 0x62, 0x6d, 0x3b,
+       0x16, 0x1b, 0x79, 0x08, 0x08, 0x77, 0x4a, 0x17,
+       0x15, 0x47, 0x58, 0x5c, 0x5d, 0x3d, 0x12, 0x36,
+       0x48, 0x5e, 0x51, 0x19, 0x6e, 0x5f, 0x64, 0x3c,
+       0x62, 0x0b, 0x00, 0x15, 0x15, 0x2e, 0x4d, 0x5c,
+       0x1b, 0x0a, 0x51, 0x1b, 0x13, 0x68, 0x14, 0x28,
+       0x26, 0x69, 0x27, 0x52, 0x13, 0x1e, 0x19, 0x31,
+       0x42, 0x0e, 0x3a, 0x29, 0x07, 0x41, 0x27, 0x40,
+       0x4e, 0x68, 0x68, 0x78, 0x64, 0x36, 0x52, 0x7a,
+       0x07, 0x6e, 0x46, 0x63, 0x4a, 0x6c, 0x5b, 0x4c,
+       0x74, 0x14, 0x14, 0x76, 0x15, 0x2d, 0x79, 0x10,
+       0x65, 0x48, 0x60, 0x6a, 0x1c, 0x65, 0x74, 0x73,
+       0x56, 0x3c, 0x4b, 0x34, 0x20, 0x24, 0x36, 0x0d,
+       0x3c, 0x59, 0x0f, 0x46, 0x47, 0x4a, 0x53, 0x62,
+       0x63, 0x44, 0x22, 0x39, 0x15, 0x68, 0x60, 0x7b,
+       0x73, 0x0f, 0x34, 0x79, 0x6a, 0x76, 0x4e, 0x0f,
+       0x02, 0x5d, 0x09, 0x73, 0x76, 0x18, 0x48, 0x4f,
+       0x72, 0x19, 0x71, 0x3c, 0x6e, 0x0b, 0x3b, 0x45,
+       0x1c, 0x3e, 0x1b, 0x46, 0x74, 0x03, 0x5d, 0x0a,
+       0x01, 0x62, 0x04, 0x2f, 0x6f, 0x03, 0x4c, 0x36,
+       0x5f, 0x6a, 0x0c, 0x79, 0x34, 0x4f, 0x42, 0x6c,
+       0x66, 0x21, 0x26, 0x21, 0x4a, 0x0e, 0x3e, 0x73,
+       0x45, 0x43, 0x5e, 0x2a, 0x63, 0x32, 0x0b, 0x66,
+       0x09, 0x46, 0x15, 0x46, 0x1c, 0x46, 0x10, 0x5b,
+       0x09, 0x75, 0x67, 0x7f, 0x51, 0x6d, 0x12, 0x65,
+       0x0d, 0x52, 0x06, 0x28, 0x61, 0x0f, 0x4e, 0x51,
+       0x61, 0x75, 0x1f, 0x26, 0x31, 0x66, 0x34, 0x67,
+       0x5d, 0x59, 0x2e, 0x18, 0x40, 0x63, 0x16, 0x12,
+       0x49, 0x60, 0x1c, 0x62, 0x30, 0x21, 0x5c, 0x69,
+       0x2c, 0x29, 0x1c, 0x3b, 0x3d, 0x13, 0x49, 0x4d,
+       0x1f, 0x5f, 0x1d, 0x0a, 0x54, 0x1e, 0x52, 0x27,
+       0x79, 0x79, 0x31, 0x03, 0x67, 0x02, 0x6a, 0x63,
+       0x36, 0x5d, 0x38, 0x48, 0x1b, 0x4e, 0x5b, 0x63,
+       0x7b, 0x7b, 0x4e, 0x71, 0x45, 0x37, 0x34, 0x44,
+       0x03, 0x51, 0x31, 0x23, 0x0b, 0x18, 0x6d, 0x7f,
+       0x76, 0x21, 0x17, 0x27, 0x45, 0x09, 0x0c, 0x2e,
+       0x69, 0x74, 0x59, 0x2b, 0x75, 0x0c, 0x34, 0x0a,
+       0x3a, 0x27, 0x25, 0x7b, 0x45, 0x0d, 0x59, 0x2f,
+       0x2b, 0x57, 0x7e, 0x1f, 0x05, 0x62, 0x28, 0x79,
+       0x7e, 0x1d, 0x58, 0x30, 0x35, 0x06, 0x67, 0x5b,
+       0x7a, 0x00, 0x34, 0x32, 0x33, 0x2f, 0x68, 0x4b,
+       0x76, 0x38, 0x7e, 0x58, 0x50, 0x56, 0x6d, 0x1f,
+       0x14, 0x6f, 0x77, 0x39, 0x71, 0x35, 0x08, 0x44,
+       0x3b, 0x09, 0x16, 0x19, 0x13, 0x1c, 0x67, 0x7d,
+       0x7f, 0x56, 0x7e, 0x31, 0x6b, 0x67, 0x44, 0x76,
+       0x53, 0x55, 0x6d, 0x3d, 0x13, 0x3b, 0x37, 0x1c,
+       0x0b, 0x21, 0x58, 0x03, 0x31, 0x2d, 0x3d, 0x6c,
+       0x01, 0x6d, 0x08, 0x1d, 0x03, 0x4d, 0x6e, 0x63,
+       0x4c, 0x21, 0x2c, 0x57, 0x48, 0x07, 0x52, 0x2a,
+       0x6d, 0x64, 0x0d, 0x56, 0x7e, 0x08, 0x3c, 0x1b,
+       0x28, 0x04, 0x0f, 0x58, 0x0d, 0x6a, 0x73, 0x70,
+       0x28, 0x0c, 0x6e, 0x1e, 0x09, 0x39, 0x46, 0x3e,
+       0x62, 0x08, 0x72, 0x52, 0x42, 0x02, 0x78, 0x62,
+       0x31, 0x73, 0x0d, 0x4d, 0x5c, 0x07, 0x64, 0x17,
+       0x55, 0x29, 0x60, 0x07, 0x67, 0x59, 0x63, 0x78,
+       0x73, 0x17, 0x42, 0x27, 0x0e, 0x77, 0x15, 0x60,
+       0x07, 0x46, 0x53, 0x6a, 0x05, 0x38, 0x12, 0x14,
+       0x1f, 0x1b, 0x11, 0x6a, 0x1d, 0x02, 0x3c, 0x05,
+       0x75, 0x6d, 0x51, 0x16, 0x10, 0x6f, 0x02, 0x46,
+       0x39, 0x2e, 0x37, 0x47, 0x7a, 0x5b, 0x39, 0x15,
+       0x14, 0x4b, 0x77, 0x0b, 0x19, 0x24, 0x4d, 0x36,
+       0x33, 0x4c, 0x6a, 0x53, 0x79, 0x69, 0x57, 0x17,
+       0x10, 0x75, 0x1f, 0x72, 0x08, 0x71, 0x58, 0x14,
+       0x46, 0x4a, 0x6f, 0x3c, 0x30, 0x34, 0x5b, 0x36,
+       0x42, 0x13, 0x11, 0x45, 0x78, 0x5a, 0x57, 0x68,
+       0x33, 0x4b, 0x21, 0x00, 0x06, 0x6b, 0x3d, 0x17,
+       0x0e, 0x6a, 0x2b, 0x2a, 0x32, 0x3a, 0x2a, 0x46,
+       0x79, 0x1f, 0x56, 0x40, 0x43, 0x36, 0x18, 0xf7,
+};
+
+static const snd_seq_event_t event5[] = {
+       /* SysEx over 4 events */
+       SYSEX_EVENT_RAW(sysex5_1, 256, 0),
+       SYSEX_EVENT_RAW(sysex5_1, 256, 256),
+       SYSEX_EVENT_RAW(sysex5_1, 256, 512),
+       SYSEX_EVENT_RAW(sysex5_1, 256, 768),
+};
+
+static const snd_seq_event_t event5_expect[] = {
+       SYSEX_EVENT(sysex5_1),
+};
+
+static const struct midi_write_test midi5 = BLE_WRITE_TEST_INIT(event5, event5_expect);
+
+static void test_midi_writer(gconstpointer data)
+{
+       const struct midi_write_test *midi_test = data;
+       struct midi_write_parser midi_out;
+       struct midi_read_parser midi_in;
+       size_t i; /* event counter */
+       size_t j; /* test counter */
+       struct midi_data {
+               size_t events_tested;
+               const struct midi_write_test *midi_test;
+               struct midi_read_parser *midi_in;
+       } midi_data;
+
+       void compare_events_cb(const struct midi_write_parser *parser, void *user_data) {
+               struct midi_data *midi_data = user_data;
+               const struct midi_write_test *midi_test = midi_data->midi_test;
+               struct midi_read_parser *midi_in = midi_data->midi_in;
+               size_t i = 0;
+
+               midi_read_reset(midi_in);
+
+               while (i < midi_write_data_size(parser)) {
+                       snd_seq_event_t ev;
+                       size_t count;
+
+                       snd_seq_ev_clear(&ev);
+
+                       count = midi_read_raw(midi_in, midi_write_data(parser) + i,
+                                             midi_write_data_size(parser) - i, &ev);
+
+                       g_assert_cmpuint(count, >, 0);
+
+                       if (ev.type != SND_SEQ_EVENT_NONE){
+                               g_assert_cmpint(midi_data->events_tested,
+                                               <,
+                                               midi_test->event_expect_size);
+                               compare_events(&midi_test->event_expect[midi_data->events_tested],
+                                              &ev);
+                               midi_data->events_tested++;
+                       }
+
+                       i += count;
+               }
+       };
+
+       midi_read_init(&midi_in);
+
+       for (j = 0; j < NUM_WRITE_TESTS; j++) {
+
+               /* Range of test for different MTU sizes. The spec specifies
+                  sizes of packet as MTU - 3 */
+               midi_write_init(&midi_out, g_random_int_range(5, 512));
+
+               midi_data.events_tested = 0;
+               midi_data.midi_test = midi_test;
+               midi_data.midi_in = &midi_in;
+
+               for (i = 0; i < midi_test->event_size; i++)
+                       midi_read_ev(&midi_out, &midi_test->event[i],
+                                    compare_events_cb, &midi_data);
+
+               if (midi_write_has_data(&midi_out))
+                       compare_events_cb(&midi_out, &midi_data);
+
+               g_assert_cmpint(midi_data.events_tested,
+                               ==,
+                               midi_test->event_expect_size);
+
+               midi_write_free(&midi_out);
+       }
+       midi_read_free(&midi_in);
+
+       tester_test_passed();
+}
+
+int main(int argc, char *argv[])
+{
+       tester_init(&argc, &argv);
+
+       tester_add("Raw BLE packets read",
+                  &midi1, NULL, test_midi_reader, NULL);
+       tester_add("Raw BLE packets SysEx read",
+                  &midi2, NULL, test_midi_reader, NULL);
+       tester_add("ALSA Seq events to Raw BLE packets",
+                  &midi3, NULL, test_midi_writer, NULL);
+       tester_add("ALSA SysEx events to Raw BLE packets",
+                  &midi4, NULL, test_midi_writer, NULL);
+       tester_add("Split ALSA SysEx events to raw BLE packets",
+                  &midi5, NULL, test_midi_writer, NULL);
+
+       return tester_run();
+}